You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
11983 lines
392 KiB
C++
11983 lines
392 KiB
C++
|
|
#include "zxing-all-in-one.h"
|
|
|
|
// file: zxing/BarcodeFormat.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Created by Christian Brunschen on 13/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/BarcodeFormat.h>
|
|
|
|
namespace zxing {
|
|
|
|
const char *barcodeFormatNames[] = {
|
|
"None",
|
|
"QR_CODE",
|
|
"DATA_MATRIX",
|
|
"UPC_E",
|
|
"UPC_A",
|
|
"EAN_8",
|
|
"EAN_13",
|
|
"CODE_128",
|
|
"CODE_39",
|
|
"ITF"
|
|
};
|
|
|
|
}
|
|
|
|
// file: zxing/Binarizer.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Binarizer.cpp
|
|
* zxing
|
|
*
|
|
* Created by Ralf Kistner on 16/10/2009.
|
|
* Copyright 2008 ZXing authors All rights reserved.
|
|
* Modified by Lukasz Warchol on 02/02/2010.
|
|
*
|
|
* 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/Binarizer.h>
|
|
|
|
namespace zxing {
|
|
|
|
Binarizer::Binarizer(Ref<LuminanceSource> source) : source_(source) {
|
|
}
|
|
|
|
Binarizer::~Binarizer() {
|
|
}
|
|
|
|
Ref<LuminanceSource> Binarizer::getLuminanceSource() const {
|
|
return source_;
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/BinaryBitmap.cpp
|
|
|
|
/*
|
|
* BinaryBitmap.cpp
|
|
* zxing
|
|
*
|
|
* Copyright 2010 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/BinaryBitmap.h>
|
|
|
|
namespace zxing {
|
|
|
|
BinaryBitmap::BinaryBitmap(Ref<Binarizer> binarizer) : binarizer_(binarizer) {
|
|
|
|
}
|
|
|
|
BinaryBitmap::~BinaryBitmap() {
|
|
}
|
|
|
|
Ref<BitArray> BinaryBitmap::getBlackRow(int y, Ref<BitArray> row) {
|
|
return binarizer_->getBlackRow(y, row);
|
|
}
|
|
|
|
Ref<BitMatrix> BinaryBitmap::getBlackMatrix() {
|
|
return binarizer_->getBlackMatrix();
|
|
}
|
|
|
|
int BinaryBitmap::getWidth() const {
|
|
return getLuminanceSource()->getWidth();
|
|
}
|
|
|
|
int BinaryBitmap::getHeight() const {
|
|
return getLuminanceSource()->getHeight();
|
|
}
|
|
|
|
Ref<LuminanceSource> BinaryBitmap::getLuminanceSource() const {
|
|
return binarizer_->getLuminanceSource();
|
|
}
|
|
|
|
|
|
bool BinaryBitmap::isCropSupported() const {
|
|
return getLuminanceSource()->isCropSupported();
|
|
}
|
|
|
|
Ref<BinaryBitmap> BinaryBitmap::crop(int left, int top, int width, int height) {
|
|
return Ref<BinaryBitmap> (new BinaryBitmap(binarizer_->createBinarizer(getLuminanceSource()->crop(left, top, width, height))));
|
|
}
|
|
|
|
bool BinaryBitmap::isRotateSupported() const {
|
|
return getLuminanceSource()->isRotateSupported();
|
|
}
|
|
|
|
Ref<BinaryBitmap> BinaryBitmap::rotateCounterClockwise() {
|
|
return Ref<BinaryBitmap> (new BinaryBitmap(binarizer_->createBinarizer(getLuminanceSource()->rotateCounterClockwise())));
|
|
}
|
|
}
|
|
|
|
// file: zxing/DecodeHints.cpp
|
|
|
|
/*
|
|
* DecodeHintType.cpp
|
|
* zxing
|
|
*
|
|
* Copyright 2010 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/DecodeHints.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
namespace zxing {
|
|
|
|
const DecodeHintType DecodeHints::CHARACTER_SET;
|
|
|
|
const DecodeHints DecodeHints::PRODUCT_HINT(
|
|
BARCODEFORMAT_UPC_E_HINT |
|
|
BARCODEFORMAT_UPC_A_HINT |
|
|
BARCODEFORMAT_EAN_8_HINT |
|
|
BARCODEFORMAT_EAN_13_HINT);
|
|
|
|
const DecodeHints DecodeHints::ONED_HINT(
|
|
BARCODEFORMAT_UPC_E_HINT |
|
|
BARCODEFORMAT_UPC_A_HINT |
|
|
BARCODEFORMAT_EAN_8_HINT |
|
|
BARCODEFORMAT_EAN_13_HINT |
|
|
BARCODEFORMAT_CODE_128_HINT |
|
|
BARCODEFORMAT_CODE_39_HINT |
|
|
BARCODEFORMAT_ITF_HINT);
|
|
|
|
const DecodeHints DecodeHints::DEFAULT_HINT(
|
|
BARCODEFORMAT_UPC_E_HINT |
|
|
BARCODEFORMAT_UPC_A_HINT |
|
|
BARCODEFORMAT_EAN_8_HINT |
|
|
BARCODEFORMAT_EAN_13_HINT |
|
|
BARCODEFORMAT_CODE_128_HINT |
|
|
BARCODEFORMAT_CODE_39_HINT |
|
|
BARCODEFORMAT_ITF_HINT |
|
|
BARCODEFORMAT_DATA_MATRIX_HINT |
|
|
BARCODEFORMAT_QR_CODE_HINT);
|
|
|
|
DecodeHints::DecodeHints() {
|
|
hints = 0;
|
|
}
|
|
|
|
DecodeHints::DecodeHints(DecodeHintType init) {
|
|
hints = init;
|
|
}
|
|
|
|
void DecodeHints::addFormat(BarcodeFormat toadd) {
|
|
switch (toadd) {
|
|
case BarcodeFormat_QR_CODE: hints |= BARCODEFORMAT_QR_CODE_HINT; break;
|
|
case BarcodeFormat_DATA_MATRIX: hints |= BARCODEFORMAT_DATA_MATRIX_HINT; break;
|
|
case BarcodeFormat_UPC_E: hints |= BARCODEFORMAT_UPC_E_HINT; break;
|
|
case BarcodeFormat_UPC_A: hints |= BARCODEFORMAT_UPC_A_HINT; break;
|
|
case BarcodeFormat_EAN_8: hints |= BARCODEFORMAT_EAN_8_HINT; break;
|
|
case BarcodeFormat_EAN_13: hints |= BARCODEFORMAT_EAN_13_HINT; break;
|
|
case BarcodeFormat_CODE_128: hints |= BARCODEFORMAT_CODE_128_HINT; break;
|
|
case BarcodeFormat_CODE_39: hints |= BARCODEFORMAT_CODE_39_HINT; break;
|
|
case BarcodeFormat_ITF: hints |= BARCODEFORMAT_ITF_HINT; break;
|
|
default: throw IllegalArgumentException("Unrecognizd barcode format");
|
|
}
|
|
}
|
|
|
|
bool DecodeHints::containsFormat(BarcodeFormat tocheck) const {
|
|
DecodeHintType checkAgainst;
|
|
switch (tocheck) {
|
|
case BarcodeFormat_QR_CODE: checkAgainst = BARCODEFORMAT_QR_CODE_HINT; break;
|
|
case BarcodeFormat_DATA_MATRIX: checkAgainst = BARCODEFORMAT_DATA_MATRIX_HINT; break;
|
|
case BarcodeFormat_UPC_E: checkAgainst = BARCODEFORMAT_UPC_E_HINT; break;
|
|
case BarcodeFormat_UPC_A: checkAgainst = BARCODEFORMAT_UPC_A_HINT; break;
|
|
case BarcodeFormat_EAN_8: checkAgainst = BARCODEFORMAT_EAN_8_HINT; break;
|
|
case BarcodeFormat_EAN_13: checkAgainst = BARCODEFORMAT_EAN_13_HINT; break;
|
|
case BarcodeFormat_CODE_128: checkAgainst = BARCODEFORMAT_CODE_128_HINT; break;
|
|
case BarcodeFormat_CODE_39: checkAgainst = BARCODEFORMAT_CODE_39_HINT; break;
|
|
case BarcodeFormat_ITF: checkAgainst = BARCODEFORMAT_ITF_HINT; break;
|
|
default: throw IllegalArgumentException("Unrecognizd barcode format");
|
|
}
|
|
return (hints & checkAgainst);
|
|
}
|
|
|
|
void DecodeHints::setTryHarder(bool toset) {
|
|
if (toset) {
|
|
hints |= TRYHARDER_HINT;
|
|
} else {
|
|
hints &= ~TRYHARDER_HINT;
|
|
}
|
|
}
|
|
|
|
bool DecodeHints::getTryHarder() const {
|
|
return (hints & TRYHARDER_HINT);
|
|
}
|
|
|
|
void DecodeHints::setResultPointCallback(Ref<ResultPointCallback> const& _callback) {
|
|
callback = _callback;
|
|
}
|
|
|
|
Ref<ResultPointCallback> DecodeHints::getResultPointCallback() const {
|
|
return callback;
|
|
}
|
|
|
|
} /* namespace */
|
|
|
|
// file: zxing/Exception.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Exception.cpp
|
|
* ZXing
|
|
*
|
|
* Created by Christian Brunschen on 03/06/2008.
|
|
* Copyright 2008-2011 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/Exception.h>
|
|
|
|
namespace zxing {
|
|
|
|
Exception::Exception() {}
|
|
|
|
Exception::Exception(const char *msg) :
|
|
message(msg) {
|
|
}
|
|
|
|
const char* Exception::what() const throw() {
|
|
return message.c_str();
|
|
}
|
|
|
|
Exception::~Exception() throw() {
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/FormatException.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* FormatException.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 13/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/FormatException.h>
|
|
|
|
namespace zxing {
|
|
|
|
FormatException::FormatException() {}
|
|
|
|
FormatException::FormatException(const char *msg) :
|
|
ReaderException(msg) {
|
|
}
|
|
|
|
FormatException::~FormatException() throw() {
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/LuminanceSource.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* LuminanceSource.cpp
|
|
* zxing
|
|
*
|
|
* 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 <sstream>
|
|
// #include <zxing/LuminanceSource.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
namespace zxing {
|
|
|
|
LuminanceSource::LuminanceSource() {
|
|
}
|
|
|
|
LuminanceSource::~LuminanceSource() {
|
|
}
|
|
|
|
bool LuminanceSource::isCropSupported() const {
|
|
return false;
|
|
}
|
|
|
|
Ref<LuminanceSource> LuminanceSource::crop(int left, int top, int width, int height) {
|
|
(void)left;
|
|
(void)top;
|
|
(void)width;
|
|
(void)height;
|
|
throw IllegalArgumentException("This luminance source does not support cropping.");
|
|
}
|
|
|
|
bool LuminanceSource::isRotateSupported() const {
|
|
return false;
|
|
}
|
|
|
|
Ref<LuminanceSource> LuminanceSource::rotateCounterClockwise() {
|
|
throw IllegalArgumentException("This luminance source does not support rotation.");
|
|
}
|
|
|
|
LuminanceSource::operator std::string() {
|
|
unsigned char* row = 0;
|
|
std::ostringstream oss;
|
|
for (int y = 0; y < getHeight(); y++) {
|
|
row = getRow(y, row);
|
|
for (int x = 0; x < getWidth(); x++) {
|
|
int luminance = row[x] & 0xFF;
|
|
char c;
|
|
if (luminance < 0x40) {
|
|
c = '#';
|
|
} else if (luminance < 0x80) {
|
|
c = '+';
|
|
} else if (luminance < 0xC0) {
|
|
c = '.';
|
|
} else {
|
|
c = ' ';
|
|
}
|
|
oss << c;
|
|
}
|
|
oss << '\n';
|
|
}
|
|
delete [] row;
|
|
return oss.str();
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
// file: zxing/MultiFormatReader.cpp
|
|
|
|
/*
|
|
* MultiFormatBarcodeReader.cpp
|
|
* ZXing
|
|
*
|
|
* Created by Lukasz Warchol on 10-01-26.
|
|
* Modified by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/MultiFormatReader.h>
|
|
// #include <zxing/qrcode/QRCodeReader.h>
|
|
// #include <zxing/datamatrix/DataMatrixReader.h>
|
|
// #include <zxing/oned/MultiFormatUPCEANReader.h>
|
|
// #include <zxing/oned/MultiFormatOneDReader.h>
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
MultiFormatReader::MultiFormatReader() {
|
|
|
|
}
|
|
|
|
Ref<Result> MultiFormatReader::decode(Ref<BinaryBitmap> image) {
|
|
setHints(DecodeHints::DEFAULT_HINT);
|
|
return decodeInternal(image);
|
|
}
|
|
|
|
Ref<Result> MultiFormatReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
|
setHints(hints);
|
|
return decodeInternal(image);
|
|
}
|
|
|
|
Ref<Result> MultiFormatReader::decodeWithState(Ref<BinaryBitmap> image) {
|
|
// Make sure to set up the default state so we don't crash
|
|
if (readers_.size() == 0) {
|
|
setHints(DecodeHints::DEFAULT_HINT);
|
|
}
|
|
return decodeInternal(image);
|
|
}
|
|
|
|
void MultiFormatReader::setHints(DecodeHints hints) {
|
|
hints_ = hints;
|
|
readers_.clear();
|
|
bool tryHarder = hints.getTryHarder();
|
|
|
|
bool addOneDReader = hints.containsFormat(BarcodeFormat_UPC_E) ||
|
|
hints.containsFormat(BarcodeFormat_UPC_A) ||
|
|
hints.containsFormat(BarcodeFormat_EAN_8) ||
|
|
hints.containsFormat(BarcodeFormat_EAN_13) ||
|
|
hints.containsFormat(BarcodeFormat_CODE_128) ||
|
|
hints.containsFormat(BarcodeFormat_CODE_39) ||
|
|
hints.containsFormat(BarcodeFormat_ITF);
|
|
if (addOneDReader && !tryHarder) {
|
|
readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints)));
|
|
}
|
|
if (hints.containsFormat(BarcodeFormat_QR_CODE)) {
|
|
readers_.push_back(Ref<Reader>(new zxing::qrcode::QRCodeReader()));
|
|
}
|
|
if (hints.containsFormat(BarcodeFormat_DATA_MATRIX)) {
|
|
readers_.push_back(Ref<Reader>(new zxing::datamatrix::DataMatrixReader()));
|
|
}
|
|
//TODO: add PDF417 here once PDF417 reader is implemented
|
|
if (addOneDReader && tryHarder) {
|
|
readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints)));
|
|
}
|
|
if (readers_.size() == 0) {
|
|
if (!tryHarder) {
|
|
readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints)));
|
|
}
|
|
readers_.push_back(Ref<Reader>(new zxing::qrcode::QRCodeReader()));
|
|
if (tryHarder) {
|
|
readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints)));
|
|
}
|
|
}
|
|
}
|
|
|
|
Ref<Result> MultiFormatReader::decodeInternal(Ref<BinaryBitmap> image) {
|
|
for (unsigned int i = 0; i < readers_.size(); i++) {
|
|
try {
|
|
return readers_[i]->decode(image, hints_);
|
|
} catch (ReaderException const& re) {
|
|
// continue
|
|
}
|
|
}
|
|
throw ReaderException("No code detected");
|
|
}
|
|
|
|
MultiFormatReader::~MultiFormatReader() {
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/NotFoundException.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 20011 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.
|
|
*/
|
|
|
|
// #include <zxing/NotFoundException.h>
|
|
|
|
namespace zxing {
|
|
|
|
NotFoundException::NotFoundException(const char *msg)
|
|
: ReaderException(msg) {}
|
|
|
|
NotFoundException::~NotFoundException() throw() {
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/Reader.cpp
|
|
|
|
/*
|
|
* Reader.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 13/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/Reader.h>
|
|
|
|
namespace zxing {
|
|
|
|
Reader::~Reader() { }
|
|
|
|
Ref<Result> Reader::decode(Ref<BinaryBitmap> image) {
|
|
return decode(image, DecodeHints::DEFAULT_HINT);
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/ReaderException.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* ReaderException.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 13/05/2008.
|
|
* Copyright 2008-2011 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/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
|
|
ReaderException::ReaderException() {}
|
|
|
|
ReaderException::ReaderException(const char *msg) :
|
|
Exception(msg) {
|
|
}
|
|
|
|
ReaderException::~ReaderException() throw() {
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/Result.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Result.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 13/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/Result.h>
|
|
|
|
namespace zxing {
|
|
using namespace std;
|
|
|
|
Result::Result(Ref<String> text, ArrayRef<unsigned char> rawBytes, std::vector<Ref<ResultPoint> > resultPoints,
|
|
BarcodeFormat format) :
|
|
text_(text), rawBytes_(rawBytes), resultPoints_(resultPoints), format_(format) {
|
|
}
|
|
|
|
Result::~Result() {
|
|
}
|
|
|
|
Ref<String> Result::getText() {
|
|
return text_;
|
|
}
|
|
|
|
ArrayRef<unsigned char> Result::getRawBytes() {
|
|
return rawBytes_;
|
|
}
|
|
|
|
const std::vector<Ref<ResultPoint> >& Result::getResultPoints() const {
|
|
return resultPoints_;
|
|
}
|
|
|
|
std::vector<Ref<ResultPoint> >& Result::getResultPoints() {
|
|
return resultPoints_;
|
|
}
|
|
|
|
BarcodeFormat Result::getBarcodeFormat() const {
|
|
return format_;
|
|
}
|
|
|
|
ostream& operator<<(ostream &out, Result& result) {
|
|
if (result.text_ != 0) {
|
|
out << result.text_->getText();
|
|
} else {
|
|
out << "[" << result.rawBytes_->size() << " bytes]";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/ResultPoint.cpp
|
|
|
|
/*
|
|
* ResultPoint.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 13/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/ResultPoint.h>
|
|
// #include <math.h>
|
|
|
|
namespace zxing {
|
|
|
|
ResultPoint::ResultPoint() : posX_(0), posY_(0) {}
|
|
|
|
ResultPoint::ResultPoint(float x, float y) : posX_(x), posY_(y) {}
|
|
|
|
ResultPoint::~ResultPoint() {}
|
|
|
|
float ResultPoint::getX() const {
|
|
return posX_;
|
|
}
|
|
|
|
float ResultPoint::getY() const {
|
|
return posY_;
|
|
}
|
|
|
|
bool ResultPoint::equals(Ref<ResultPoint> other) {
|
|
return posX_ == other->getX() && posY_ == other->getY();
|
|
}
|
|
|
|
/**
|
|
* <p>Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and
|
|
* BC < AC and the angle between BC and BA is less than 180 degrees.
|
|
*/
|
|
void ResultPoint::orderBestPatterns(std::vector<Ref<ResultPoint> > &patterns) {
|
|
// Find distances between pattern centers
|
|
float zeroOneDistance = distance(patterns[0]->getX(), patterns[1]->getX(),patterns[0]->getY(), patterns[1]->getY());
|
|
float oneTwoDistance = distance(patterns[1]->getX(), patterns[2]->getX(),patterns[1]->getY(), patterns[2]->getY());
|
|
float zeroTwoDistance = distance(patterns[0]->getX(), patterns[2]->getX(),patterns[0]->getY(), patterns[2]->getY());
|
|
|
|
Ref<ResultPoint> pointA, pointB, pointC;
|
|
// Assume one closest to other two is B; A and C will just be guesses at first
|
|
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
|
|
pointB = patterns[0];
|
|
pointA = patterns[1];
|
|
pointC = patterns[2];
|
|
} else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
|
|
pointB = patterns[1];
|
|
pointA = patterns[0];
|
|
pointC = patterns[2];
|
|
} else {
|
|
pointB = patterns[2];
|
|
pointA = patterns[0];
|
|
pointC = patterns[1];
|
|
}
|
|
|
|
// Use cross product to figure out whether A and C are correct or flipped.
|
|
// This asks whether BC x BA has a positive z component, which is the arrangement
|
|
// we want for A, B, C. If it's negative, then we've got it flipped around and
|
|
// should swap A and C.
|
|
if (crossProductZ(pointA, pointB, pointC) < 0.0f) {
|
|
Ref<ResultPoint> temp = pointA;
|
|
pointA = pointC;
|
|
pointC = temp;
|
|
}
|
|
|
|
patterns[0] = pointA;
|
|
patterns[1] = pointB;
|
|
patterns[2] = pointC;
|
|
}
|
|
|
|
float ResultPoint::distance(Ref<ResultPoint> point1, Ref<ResultPoint> point2) {
|
|
return distance(point1->getX(), point1->getY(), point2->getX(), point2->getY());
|
|
}
|
|
|
|
float ResultPoint::distance(float x1, float x2, float y1, float y2) {
|
|
float xDiff = x1 - x2;
|
|
float yDiff = y1 - y2;
|
|
return (float) sqrt((double) (xDiff * xDiff + yDiff * yDiff));
|
|
}
|
|
|
|
float ResultPoint::crossProductZ(Ref<ResultPoint> pointA, Ref<ResultPoint> pointB, Ref<ResultPoint> pointC) {
|
|
float bX = pointB->getX();
|
|
float bY = pointB->getY();
|
|
return ((pointC->getX() - bX) * (pointA->getY() - bY)) - ((pointC->getY() - bY) * (pointA->getX() - bX));
|
|
}
|
|
}
|
|
|
|
// file: zxing/ResultPointCallback.cpp
|
|
|
|
/*
|
|
* ResultPointCallback.cpp
|
|
* zxing
|
|
*
|
|
* Copyright 2010 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/ResultPointCallback.h>
|
|
|
|
namespace zxing {
|
|
|
|
ResultPointCallback::~ResultPointCallback() {}
|
|
|
|
}
|
|
|
|
// file: zxing/common/Array.cpp
|
|
|
|
/*
|
|
* Array.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 07/05/2008.
|
|
* Copyright 2008 Google UK. 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/common/Array.h>
|
|
|
|
|
|
// file: zxing/common/BitArray.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2010 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/common/BitArray.h>
|
|
|
|
using namespace std;
|
|
|
|
namespace zxing {
|
|
|
|
|
|
size_t BitArray::wordsForBits(size_t bits) {
|
|
int arraySize = (bits + bitsPerWord_ - 1) >> logBits_;
|
|
return arraySize;
|
|
}
|
|
|
|
BitArray::BitArray(size_t size) :
|
|
size_(size), bits_(wordsForBits(size), (const unsigned int)0) {
|
|
}
|
|
|
|
BitArray::~BitArray() {
|
|
}
|
|
|
|
size_t BitArray::getSize() {
|
|
return size_;
|
|
}
|
|
|
|
void BitArray::setBulk(size_t i, unsigned int newBits) {
|
|
bits_[i >> logBits_] = newBits;
|
|
}
|
|
|
|
void BitArray::setRange(int start, int end) {
|
|
if (end < start) {
|
|
throw IllegalArgumentException("invalid call to BitArray::setRange");
|
|
}
|
|
if (end == start) {
|
|
return;
|
|
}
|
|
end--; // will be easier to treat this as the last actually set bit -- inclusive
|
|
int firstInt = start >> 5;
|
|
int lastInt = end >> 5;
|
|
for (int i = firstInt; i <= lastInt; i++) {
|
|
int firstBit = i > firstInt ? 0 : start & 0x1F;
|
|
int lastBit = i < lastInt ? 31 : end & 0x1F;
|
|
int mask;
|
|
if (firstBit == 0 && lastBit == 31) {
|
|
mask = -1;
|
|
} else {
|
|
mask = 0;
|
|
for (int j = firstBit; j <= lastBit; j++) {
|
|
mask |= 1 << j;
|
|
}
|
|
}
|
|
bits_[i] |= mask;
|
|
}
|
|
}
|
|
|
|
void BitArray::clear() {
|
|
size_t max = bits_.size();
|
|
for (size_t i = 0; i < max; i++) {
|
|
bits_[i] = 0;
|
|
}
|
|
}
|
|
|
|
bool BitArray::isRange(size_t start, size_t end, bool value) {
|
|
if (end < start) {
|
|
throw IllegalArgumentException("end must be after start");
|
|
}
|
|
if (end == start) {
|
|
return true;
|
|
}
|
|
// treat the 'end' as inclusive, rather than exclusive
|
|
end--;
|
|
size_t firstWord = start >> logBits_;
|
|
size_t lastWord = end >> logBits_;
|
|
for (size_t i = firstWord; i <= lastWord; i++) {
|
|
size_t firstBit = i > firstWord ? 0 : start & bitsMask_;
|
|
size_t lastBit = i < lastWord ? bitsPerWord_ - 1: end & bitsMask_;
|
|
unsigned int mask;
|
|
if (firstBit == 0 && lastBit == bitsPerWord_ - 1) {
|
|
mask = numeric_limits<unsigned int>::max();
|
|
} else {
|
|
mask = 0;
|
|
for (size_t j = firstBit; j <= lastBit; j++) {
|
|
mask |= 1 << j;
|
|
}
|
|
}
|
|
if (value) {
|
|
if ((bits_[i] & mask) != mask) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if ((bits_[i] & mask) != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
vector<unsigned int>& BitArray::getBitArray() {
|
|
return bits_;
|
|
}
|
|
|
|
void BitArray::reverse() {
|
|
std::vector<unsigned int> newBits(bits_.size(),(const unsigned int) 0);
|
|
for (size_t i = 0; i < size_; i++) {
|
|
if (get(size_ - i - 1)) {
|
|
newBits[i >> logBits_] |= 1<< (i & bitsMask_);
|
|
}
|
|
}
|
|
bits_ = newBits;
|
|
}
|
|
}
|
|
|
|
// file: zxing/common/BitMatrix.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2010 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/common/BitMatrix.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
// #include <iostream>
|
|
// #include <sstream>
|
|
// #include <string>
|
|
|
|
using std::ostream;
|
|
using std::ostringstream;
|
|
|
|
using zxing::BitMatrix;
|
|
using zxing::BitArray;
|
|
using zxing::Ref;
|
|
|
|
namespace {
|
|
size_t wordsForSize(size_t width,
|
|
size_t height,
|
|
unsigned int bitsPerWord,
|
|
unsigned int logBits) {
|
|
size_t bits = width * height;
|
|
int arraySize = (bits + bitsPerWord - 1) >> logBits;
|
|
return arraySize;
|
|
}
|
|
}
|
|
|
|
BitMatrix::BitMatrix(size_t dimension) :
|
|
width_(dimension), height_(dimension), words_(0), bits_(NULL) {
|
|
words_ = wordsForSize(width_, height_, bitsPerWord, logBits);
|
|
bits_ = new unsigned int[words_];
|
|
clear();
|
|
}
|
|
|
|
BitMatrix::BitMatrix(size_t width, size_t height) :
|
|
width_(width), height_(height), words_(0), bits_(NULL) {
|
|
words_ = wordsForSize(width_, height_, bitsPerWord, logBits);
|
|
bits_ = new unsigned int[words_];
|
|
clear();
|
|
}
|
|
|
|
BitMatrix::~BitMatrix() {
|
|
delete[] bits_;
|
|
}
|
|
|
|
|
|
void BitMatrix::flip(size_t x, size_t y) {
|
|
size_t offset = x + width_ * y;
|
|
bits_[offset >> logBits] ^= 1 << (offset & bitsMask);
|
|
}
|
|
|
|
void BitMatrix::clear() {
|
|
std::fill(bits_, bits_+words_, 0);
|
|
}
|
|
|
|
void BitMatrix::setRegion(size_t left, size_t top, size_t width, size_t height) {
|
|
if ((long)top < 0 || (long)left < 0) {
|
|
throw IllegalArgumentException("topI and leftJ must be nonnegative");
|
|
}
|
|
if (height < 1 || width < 1) {
|
|
throw IllegalArgumentException("height and width must be at least 1");
|
|
}
|
|
size_t right = left + width;
|
|
size_t bottom = top + height;
|
|
if (right > width_ || bottom > height_) {
|
|
throw IllegalArgumentException("top + height and left + width must be <= matrix dimension");
|
|
}
|
|
for (size_t y = top; y < bottom; y++) {
|
|
int yOffset = width_ * y;
|
|
for (size_t x = left; x < right; x++) {
|
|
size_t offset = x + yOffset;
|
|
bits_[offset >> logBits] |= 1 << (offset & bitsMask);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ref<BitArray> BitMatrix::getRow(int y, Ref<BitArray> row) {
|
|
if (row.empty() || row->getSize() < width_) {
|
|
row = new BitArray(width_);
|
|
} else {
|
|
row->clear();
|
|
}
|
|
size_t start = y * width_;
|
|
size_t end = start + width_ - 1; // end is inclusive
|
|
size_t firstWord = start >> logBits;
|
|
size_t lastWord = end >> logBits;
|
|
size_t bitOffset = start & bitsMask;
|
|
for (size_t i = firstWord; i <= lastWord; i++) {
|
|
size_t firstBit = i > firstWord ? 0 : start & bitsMask;
|
|
size_t lastBit = i < lastWord ? bitsPerWord - 1 : end & bitsMask;
|
|
unsigned int mask;
|
|
if (firstBit == 0 && lastBit == logBits) {
|
|
mask = std::numeric_limits<unsigned int>::max();
|
|
} else {
|
|
mask = 0;
|
|
for (size_t j = firstBit; j <= lastBit; j++) {
|
|
mask |= 1 << j;
|
|
}
|
|
}
|
|
row->setBulk((i - firstWord) << logBits, (bits_[i] & mask) >> bitOffset);
|
|
if (firstBit == 0 && bitOffset != 0) {
|
|
unsigned int prevBulk = row->getBitArray()[i - firstWord - 1];
|
|
prevBulk |= (bits_[i] & mask) << (bitsPerWord - bitOffset);
|
|
row->setBulk((i - firstWord - 1) << logBits, prevBulk);
|
|
}
|
|
}
|
|
return row;
|
|
}
|
|
|
|
size_t BitMatrix::getWidth() const {
|
|
return width_;
|
|
}
|
|
|
|
size_t BitMatrix::getHeight() const {
|
|
return height_;
|
|
}
|
|
|
|
size_t BitMatrix::getDimension() const {
|
|
return width_;
|
|
}
|
|
|
|
unsigned int* BitMatrix::getBits() const {
|
|
return bits_;
|
|
}
|
|
|
|
namespace zxing {
|
|
ostream& operator<<(ostream &out, const BitMatrix &bm) {
|
|
for (size_t y = 0; y < bm.height_; y++) {
|
|
for (size_t x = 0; x < bm.width_; x++) {
|
|
out << (bm.get(x, y) ? "X " : " ");
|
|
}
|
|
out << "\n";
|
|
}
|
|
return out;
|
|
}
|
|
}
|
|
|
|
const char* BitMatrix::description() {
|
|
ostringstream out;
|
|
out << *this;
|
|
return out.str().c_str();
|
|
}
|
|
|
|
// file: zxing/common/BitSource.cpp
|
|
|
|
/*
|
|
* BitSource.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 09/05/2008.
|
|
* Copyright 2008 Google UK. 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/common/BitSource.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
namespace zxing {
|
|
|
|
int BitSource::readBits(int numBits) {
|
|
if (numBits < 0 || numBits > 32) {
|
|
throw IllegalArgumentException("cannot read <1 or >32 bits");
|
|
} else if (numBits > available()) {
|
|
throw IllegalArgumentException("reading more bits than are available");
|
|
}
|
|
|
|
int result = 0;
|
|
|
|
// First, read remainder from current byte
|
|
if (bitOffset_ > 0) {
|
|
int bitsLeft = 8 - bitOffset_;
|
|
int toRead = numBits < bitsLeft ? numBits : bitsLeft;
|
|
int bitsToNotRead = bitsLeft - toRead;
|
|
int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
|
|
result = (bytes_[byteOffset_] & mask) >> bitsToNotRead;
|
|
numBits -= toRead;
|
|
bitOffset_ += toRead;
|
|
if (bitOffset_ == 8) {
|
|
bitOffset_ = 0;
|
|
byteOffset_++;
|
|
}
|
|
}
|
|
|
|
// Next read whole bytes
|
|
if (numBits > 0) {
|
|
while (numBits >= 8) {
|
|
result = (result << 8) | (bytes_[byteOffset_] & 0xFF);
|
|
byteOffset_++;
|
|
numBits -= 8;
|
|
}
|
|
|
|
|
|
// Finally read a partial byte
|
|
if (numBits > 0) {
|
|
int bitsToNotRead = 8 - numBits;
|
|
int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
|
|
result = (result << numBits) | ((bytes_[byteOffset_] & mask) >> bitsToNotRead);
|
|
bitOffset_ += numBits;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int BitSource::available() {
|
|
return 8 * (bytes_.size() - byteOffset_) - bitOffset_;
|
|
}
|
|
}
|
|
|
|
// file: zxing/common/CharacterSetECI.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2008-2011 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.
|
|
*/
|
|
|
|
// #include <zxing/common/CharacterSetECI.h>
|
|
// #include <sstream>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
using std::string;
|
|
|
|
using zxing::common::CharacterSetECI;
|
|
using zxing::IllegalArgumentException;
|
|
|
|
std::map<int, CharacterSetECI*> CharacterSetECI::VALUE_TO_ECI;
|
|
std::map<std::string, CharacterSetECI*> CharacterSetECI::NAME_TO_ECI;
|
|
|
|
const bool CharacterSetECI::inited = CharacterSetECI::init_tables();
|
|
|
|
bool CharacterSetECI::init_tables() {
|
|
addCharacterSet(0, "Cp437");
|
|
{ char const* s[] = {"ISO8859_1", "ISO-8859-1", 0};
|
|
addCharacterSet(1, s); }
|
|
addCharacterSet(2, "Cp437");
|
|
{ char const* s[] = {"ISO8859_1", "ISO-8859-1", 0};
|
|
addCharacterSet(3, s); }
|
|
addCharacterSet(4, "ISO8859_2");
|
|
addCharacterSet(5, "ISO8859_3");
|
|
addCharacterSet(6, "ISO8859_4");
|
|
addCharacterSet(7, "ISO8859_5");
|
|
addCharacterSet(8, "ISO8859_6");
|
|
addCharacterSet(9, "ISO8859_7");
|
|
addCharacterSet(10, "ISO8859_8");
|
|
addCharacterSet(11, "ISO8859_9");
|
|
addCharacterSet(12, "ISO8859_10");
|
|
addCharacterSet(13, "ISO8859_11");
|
|
addCharacterSet(15, "ISO8859_13");
|
|
addCharacterSet(16, "ISO8859_14");
|
|
addCharacterSet(17, "ISO8859_15");
|
|
addCharacterSet(18, "ISO8859_16");
|
|
{ char const* s[] = {"SJIS", "Shift_JIS", 0};
|
|
addCharacterSet(20, s ); }
|
|
return true;
|
|
}
|
|
|
|
CharacterSetECI::CharacterSetECI(int value, char const* encodingName_)
|
|
: ECI(value), encodingName(encodingName_) {}
|
|
|
|
char const* CharacterSetECI::getEncodingName() {
|
|
return encodingName;
|
|
}
|
|
|
|
void CharacterSetECI::addCharacterSet(int value, char const* encodingName) {
|
|
CharacterSetECI* eci = new CharacterSetECI(value, encodingName);
|
|
VALUE_TO_ECI[value] = eci; // can't use valueOf
|
|
NAME_TO_ECI[string(encodingName)] = eci;
|
|
}
|
|
|
|
void CharacterSetECI::addCharacterSet(int value, char const* const* encodingNames) {
|
|
CharacterSetECI* eci = new CharacterSetECI(value, encodingNames[0]);
|
|
VALUE_TO_ECI[value] = eci;
|
|
for (int i = 0; encodingNames[i]; i++) {
|
|
NAME_TO_ECI[string(encodingNames[i])] = eci;
|
|
}
|
|
}
|
|
|
|
CharacterSetECI* CharacterSetECI::getCharacterSetECIByValue(int value) {
|
|
if (value < 0 || value >= 900) {
|
|
std::ostringstream oss;
|
|
oss << "Bad ECI value: " << value;
|
|
throw IllegalArgumentException(oss.str().c_str());
|
|
}
|
|
return VALUE_TO_ECI[value];
|
|
}
|
|
|
|
CharacterSetECI* CharacterSetECI::getCharacterSetECIByName(string const& name) {
|
|
return NAME_TO_ECI[name];
|
|
}
|
|
|
|
// file: zxing/common/Counted.cpp
|
|
|
|
/*
|
|
* Counted.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 07/05/2008.
|
|
* Copyright 2008 Google UK. 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/common/Counted.h>
|
|
|
|
namespace zxing {
|
|
|
|
using namespace std;
|
|
|
|
template<class T>
|
|
ostream& operator<<(ostream &out, Ref<T>& ref) {
|
|
out << "Ref(" << (ref.object_ ? (*ref.object_) : "NULL") << ")";
|
|
return out;
|
|
}
|
|
}
|
|
|
|
// file: zxing/common/DecoderResult.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* DecoderResult.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 20/05/2008.
|
|
* Copyright 2008-2011 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/common/DecoderResult.h>
|
|
|
|
using namespace std;
|
|
using namespace zxing;
|
|
|
|
DecoderResult::DecoderResult(ArrayRef<unsigned char> rawBytes,
|
|
Ref<String> text,
|
|
ArrayRef< ArrayRef<unsigned char> >& byteSegments,
|
|
string const& ecLevel) :
|
|
rawBytes_(rawBytes),
|
|
text_(text),
|
|
byteSegments_(byteSegments),
|
|
ecLevel_(ecLevel) {}
|
|
|
|
DecoderResult::DecoderResult(ArrayRef<unsigned char> rawBytes,
|
|
Ref<String> text)
|
|
: rawBytes_(rawBytes), text_(text) {}
|
|
|
|
ArrayRef<unsigned char> DecoderResult::getRawBytes() {
|
|
return rawBytes_;
|
|
}
|
|
|
|
Ref<String> DecoderResult::getText() {
|
|
return text_;
|
|
}
|
|
|
|
// file: zxing/common/DetectorResult.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* DetectorResult.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 14/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/common/DetectorResult.h>
|
|
|
|
namespace zxing {
|
|
|
|
DetectorResult::DetectorResult(Ref<BitMatrix> bits, std::vector<Ref<ResultPoint> > points, Ref<PerspectiveTransform> transform) :
|
|
bits_(bits), points_(points), transform_(transform) {
|
|
}
|
|
|
|
Ref<BitMatrix> DetectorResult::getBits() {
|
|
return bits_;
|
|
}
|
|
|
|
std::vector<Ref<ResultPoint> > DetectorResult::getPoints() {
|
|
return points_;
|
|
}
|
|
|
|
Ref<PerspectiveTransform> DetectorResult::getTransform() {
|
|
return transform_;
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/common/ECI.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2008-2011 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.
|
|
*/
|
|
|
|
// #include <zxing/common/ECI.h>
|
|
// #include <sstream>
|
|
// #include <zxing/common/CharacterSetECI.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
using zxing::common::ECI;
|
|
using zxing::IllegalArgumentException;
|
|
|
|
ECI::ECI(int value_) : value(value_) {}
|
|
|
|
int ECI::getValue() const {
|
|
return value;
|
|
}
|
|
|
|
ECI* ECI::getECIByValue(int value) {
|
|
if (value < 0 || value > 999999) {
|
|
std::ostringstream oss;
|
|
oss << "Bad ECI value: " << value;
|
|
throw IllegalArgumentException(oss.str().c_str());
|
|
}
|
|
if (value < 900) { // Character set ECIs use 000000 - 000899
|
|
return CharacterSetECI::getCharacterSetECIByValue(value);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// file: zxing/common/EdgeDetector.cpp
|
|
|
|
/*
|
|
* EdgeDetector.cpp
|
|
* zxing
|
|
*
|
|
* Created by Ralf Kistner on 7/12/2009.
|
|
* 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/common/EdgeDetector.h>
|
|
// #include <algorithm>
|
|
// #include <cmath>
|
|
|
|
using namespace std;
|
|
|
|
namespace zxing {
|
|
namespace EdgeDetector {
|
|
|
|
void findEdgePoints(std::vector<Point>& points, const BitMatrix& image, Point start, Point end, bool invert, int skip, float deviation) {
|
|
float xdist = end.x - start.x;
|
|
float ydist = end.y - start.y;
|
|
float length = sqrt(xdist * xdist + ydist * ydist);
|
|
|
|
|
|
int var;
|
|
|
|
if (abs(xdist) > abs(ydist)) {
|
|
// Horizontal
|
|
if (xdist < 0)
|
|
skip = -skip;
|
|
|
|
var = int(abs(deviation * length / xdist));
|
|
|
|
float dy = ydist / xdist * skip;
|
|
bool left = (skip < 0) ^ invert;
|
|
int x = int(start.x);
|
|
|
|
int steps = int(xdist / skip);
|
|
for (int i = 0; i < steps; i++) {
|
|
x += skip;
|
|
if (x < 0 || x >= (int)image.getWidth())
|
|
continue; // In case we start off the edge
|
|
int my = int(start.y + dy * i);
|
|
int ey = min(my + var + 1, (int)image.getHeight() - 1);
|
|
int sy = max(my - var, 0);
|
|
for (int y = sy + 1; y < ey; y++) {
|
|
if (left) {
|
|
if (image.get(x, y) && !image.get(x, y + 1)) {
|
|
points.push_back(Point(x, y + 0.5f));
|
|
}
|
|
} else {
|
|
if (!image.get(x, y) && image.get(x, y + 1)) {
|
|
points.push_back(Point(x, y + 0.5f));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Vertical
|
|
if (ydist < 0)
|
|
skip = -skip;
|
|
|
|
var = int(abs(deviation * length / ydist));
|
|
|
|
float dx = xdist / ydist * skip;
|
|
bool down = (skip > 0) ^ invert;
|
|
int y = int(start.y);
|
|
|
|
int steps = int(ydist / skip);
|
|
for (int i = 0; i < steps; i++) {
|
|
y += skip;
|
|
if (y < 0 || y >= (int)image.getHeight())
|
|
continue; // In case we start off the edge
|
|
int mx = int(start.x + dx * i);
|
|
int ex = min(mx + var + 1, (int)image.getWidth() - 1);
|
|
int sx = max(mx - var, 0);
|
|
for (int x = sx + 1; x < ex; x++) {
|
|
if (down) {
|
|
if (image.get(x, y) && !image.get(x + 1, y)) {
|
|
points.push_back(Point(x + 0.5f, y));
|
|
}
|
|
|
|
} else {
|
|
if (!image.get(x, y) && image.get(x + 1, y)) {
|
|
points.push_back(Point(x + 0.5f, y));
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
Line findLine(const BitMatrix& image, Line estimate, bool invert, int deviation, float threshold, int skip) {
|
|
float t = threshold * threshold;
|
|
|
|
Point start = estimate.start;
|
|
Point end = estimate.end;
|
|
|
|
vector<Point> edges;
|
|
edges.clear();
|
|
findEdgePoints(edges, image, start, end, invert, skip, deviation);
|
|
|
|
int n = edges.size();
|
|
|
|
float xdist = end.x - start.x;
|
|
float ydist = end.y - start.y;
|
|
|
|
bool horizontal = abs(xdist) > abs(ydist);
|
|
|
|
float max = 0;
|
|
Line bestLine(start, end); // prepopulate with the given line, in case we can't find any line for some reason
|
|
|
|
for (int i = -deviation; i < deviation; i++) {
|
|
float x1, y1;
|
|
if (horizontal) {
|
|
y1 = start.y + i;
|
|
x1 = start.x - i * ydist / xdist;
|
|
} else {
|
|
y1 = start.y - i * xdist / ydist;
|
|
x1 = start.x + i;
|
|
}
|
|
|
|
for (int j = -deviation; j < deviation; j++) {
|
|
float x2, y2;
|
|
if (horizontal) {
|
|
y2 = end.y + j;
|
|
x2 = end.x - j * ydist / xdist;
|
|
} else {
|
|
y2 = end.y - j * xdist / ydist;
|
|
x2 = end.x + j;
|
|
}
|
|
|
|
float dx = x1 - x2;
|
|
float dy = y1 - y2;
|
|
float length = sqrt(dx * dx + dy * dy);
|
|
|
|
float score = 0;
|
|
|
|
for(int k = 0; k < n; k++) {
|
|
const Point& edge = edges[k];
|
|
float dist = ((x1 - edge.x) * dy - (y1 - edge.y) * dx) / length;
|
|
// Similar to least squares method
|
|
float s = t - dist * dist;
|
|
if (s > 0)
|
|
score += s;
|
|
}
|
|
|
|
if (score > max) {
|
|
max = score;
|
|
bestLine.start = Point(x1, y1);
|
|
bestLine.end = Point(x2, y2);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestLine;
|
|
}
|
|
|
|
Point intersection(Line a, Line b) {
|
|
float dxa = a.start.x - a.end.x;
|
|
float dxb = b.start.x - b.end.x;
|
|
float dya = a.start.y - a.end.y;
|
|
float dyb = b.start.y - b.end.y;
|
|
|
|
float p = a.start.x * a.end.y - a.start.y * a.end.x;
|
|
float q = b.start.x * b.end.y - b.start.y * b.end.x;
|
|
float denom = dxa * dyb - dya * dxb;
|
|
if(denom == 0) // Lines don't intersect
|
|
return Point(INFINITY, INFINITY);
|
|
|
|
float x = (p * dxb - dxa * q) / denom;
|
|
float y = (p * dyb - dya * q) / denom;
|
|
|
|
return Point(x, y);
|
|
}
|
|
|
|
} // namespace EdgeDetector
|
|
} // namespace zxing
|
|
|
|
// file: zxing/common/GlobalHistogramBinarizer.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* GlobalHistogramBinarizer.cpp
|
|
* zxing
|
|
*
|
|
* Copyright 2010 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/common/GlobalHistogramBinarizer.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
// #include <zxing/common/Array.h>
|
|
|
|
namespace zxing {
|
|
using namespace std;
|
|
|
|
const int LUMINANCE_BITS_25 = 5;
|
|
const int LUMINANCE_SHIFT_25 = 8 - LUMINANCE_BITS_25;
|
|
const int LUMINANCE_BUCKETS_25 = 1 << LUMINANCE_BITS_25;
|
|
|
|
GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source) :
|
|
Binarizer(source), cached_matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) {
|
|
|
|
}
|
|
|
|
GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {
|
|
}
|
|
|
|
|
|
Ref<BitArray> GlobalHistogramBinarizer::getBlackRow(int y, Ref<BitArray> row) {
|
|
if (y == cached_row_num_) {
|
|
if (cached_row_ != NULL) {
|
|
return cached_row_;
|
|
} else {
|
|
throw IllegalArgumentException("Too little dynamic range in luminance");
|
|
}
|
|
}
|
|
|
|
vector<int> histogram(LUMINANCE_BUCKETS_25, 0);
|
|
LuminanceSource& source = *getLuminanceSource();
|
|
int width = source.getWidth();
|
|
if (row == NULL || static_cast<int>(row->getSize()) < width) {
|
|
row = new BitArray(width);
|
|
} else {
|
|
row->clear();
|
|
}
|
|
|
|
//TODO(flyashi): cache this instead of allocating and deleting per row
|
|
unsigned char* row_pixels = NULL;
|
|
try {
|
|
row_pixels = new unsigned char[width];
|
|
row_pixels = source.getRow(y, row_pixels);
|
|
for (int x = 0; x < width; x++) {
|
|
histogram[row_pixels[x] >> LUMINANCE_SHIFT_25]++;
|
|
}
|
|
int blackPoint = estimate(histogram);
|
|
|
|
BitArray& array = *row;
|
|
int left = row_pixels[0];
|
|
int center = row_pixels[1];
|
|
for (int x = 1; x < width - 1; x++) {
|
|
int right = row_pixels[x + 1];
|
|
// A simple -1 4 -1 box filter with a weight of 2.
|
|
int luminance = ((center << 2) - left - right) >> 1;
|
|
if (luminance < blackPoint) {
|
|
array.set(x);
|
|
}
|
|
left = center;
|
|
center = right;
|
|
}
|
|
|
|
cached_row_ = row;
|
|
cached_row_num_ = y;
|
|
delete [] row_pixels;
|
|
return row;
|
|
} catch (IllegalArgumentException const& iae) {
|
|
// Cache the fact that this row failed.
|
|
cached_row_ = NULL;
|
|
cached_row_num_ = y;
|
|
delete [] row_pixels;
|
|
throw iae;
|
|
}
|
|
}
|
|
|
|
Ref<BitMatrix> GlobalHistogramBinarizer::getBlackMatrix() {
|
|
if (cached_matrix_ != NULL) {
|
|
return cached_matrix_;
|
|
}
|
|
|
|
// Faster than working with the reference
|
|
LuminanceSource& source = *getLuminanceSource();
|
|
int width = source.getWidth();
|
|
int height = source.getHeight();
|
|
vector<int> histogram(LUMINANCE_BUCKETS_25, 0);
|
|
|
|
// Quickly calculates the histogram by sampling four rows from the image.
|
|
// This proved to be more robust on the blackbox tests than sampling a
|
|
// diagonal as we used to do.
|
|
ArrayRef<unsigned char> ref (width);
|
|
unsigned char* row = &ref[0];
|
|
for (int y = 1; y < 5; y++) {
|
|
int rownum = height * y / 5;
|
|
int right = (width << 2) / 5;
|
|
row = source.getRow(rownum, row);
|
|
for (int x = width / 5; x < right; x++) {
|
|
histogram[row[x] >> LUMINANCE_SHIFT_25]++;
|
|
}
|
|
}
|
|
|
|
int blackPoint = estimate(histogram);
|
|
|
|
Ref<BitMatrix> matrix_ref(new BitMatrix(width, height));
|
|
BitMatrix& matrix = *matrix_ref;
|
|
for (int y = 0; y < height; y++) {
|
|
row = source.getRow(y, row);
|
|
for (int x = 0; x < width; x++) {
|
|
if (row[x] < blackPoint)
|
|
matrix.set(x, y);
|
|
}
|
|
}
|
|
|
|
cached_matrix_ = matrix_ref;
|
|
// delete [] row;
|
|
return matrix_ref;
|
|
}
|
|
|
|
int GlobalHistogramBinarizer::estimate(vector<int> &histogram) {
|
|
int numBuckets = histogram.size();
|
|
int maxBucketCount = 0;
|
|
|
|
// Find tallest peak in histogram
|
|
int firstPeak = 0;
|
|
int firstPeakSize = 0;
|
|
for (int i = 0; i < numBuckets; i++) {
|
|
if (histogram[i] > firstPeakSize) {
|
|
firstPeak = i;
|
|
firstPeakSize = histogram[i];
|
|
}
|
|
if (histogram[i] > maxBucketCount) {
|
|
maxBucketCount = histogram[i];
|
|
}
|
|
}
|
|
|
|
// Find second-tallest peak -- well, another peak that is tall and not
|
|
// so close to the first one
|
|
int secondPeak = 0;
|
|
int secondPeakScore = 0;
|
|
for (int i = 0; i < numBuckets; i++) {
|
|
int distanceToBiggest = i - firstPeak;
|
|
// Encourage more distant second peaks by multiplying by square of distance
|
|
int score = histogram[i] * distanceToBiggest * distanceToBiggest;
|
|
if (score > secondPeakScore) {
|
|
secondPeak = i;
|
|
secondPeakScore = score;
|
|
}
|
|
}
|
|
|
|
// Put firstPeak first
|
|
if (firstPeak > secondPeak) {
|
|
int temp = firstPeak;
|
|
firstPeak = secondPeak;
|
|
secondPeak = temp;
|
|
}
|
|
|
|
// Kind of arbitrary; if the two peaks are very close, then we figure there is
|
|
// so little dynamic range in the image, that discriminating black and white
|
|
// is too error-prone.
|
|
// Decoding the image/line is either pointless, or may in some cases lead to
|
|
// a false positive for 1D formats, which are relatively lenient.
|
|
// We arbitrarily say "close" is
|
|
// "<= 1/16 of the total histogram buckets apart"
|
|
if (secondPeak - firstPeak <= numBuckets >> 4) {
|
|
throw IllegalArgumentException("Too little dynamic range in luminance");
|
|
}
|
|
|
|
// Find a valley between them that is low and closer to the white peak
|
|
int bestValley = secondPeak - 1;
|
|
int bestValleyScore = -1;
|
|
for (int i = secondPeak - 1; i > firstPeak; i--) {
|
|
int fromFirst = i - firstPeak;
|
|
// Favor a "valley" that is not too close to either peak -- especially not
|
|
// the black peak -- and that has a low value of course
|
|
int score = fromFirst * fromFirst * (secondPeak - i) *
|
|
(maxBucketCount - histogram[i]);
|
|
if (score > bestValleyScore) {
|
|
bestValley = i;
|
|
bestValleyScore = score;
|
|
}
|
|
}
|
|
|
|
return bestValley << LUMINANCE_SHIFT_25;
|
|
}
|
|
|
|
Ref<Binarizer> GlobalHistogramBinarizer::createBinarizer(Ref<LuminanceSource> source) {
|
|
return Ref<Binarizer> (new GlobalHistogramBinarizer(source));
|
|
}
|
|
|
|
} // namespace zxing
|
|
|
|
// file: zxing/common/GreyscaleLuminanceSource.cpp
|
|
|
|
/*
|
|
* GreyscaleLuminanceSource.cpp
|
|
* zxing
|
|
*
|
|
* Copyright 2010 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/common/GreyscaleLuminanceSource.h>
|
|
// #include <zxing/common/GreyscaleRotatedLuminanceSource.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
namespace zxing {
|
|
|
|
GreyscaleLuminanceSource::GreyscaleLuminanceSource(unsigned char* greyData, int dataWidth,
|
|
int dataHeight, int left, int top, int width, int height) : greyData_(greyData),
|
|
dataWidth_(dataWidth), dataHeight_(dataHeight), left_(left), top_(top), width_(width),
|
|
height_(height) {
|
|
|
|
if (left + width > dataWidth || top + height > dataHeight || top < 0 || left < 0) {
|
|
throw IllegalArgumentException("Crop rectangle does not fit within image data.");
|
|
}
|
|
}
|
|
|
|
unsigned char* GreyscaleLuminanceSource::getRow(int y, unsigned char* row) {
|
|
if (y < 0 || y >= this->getHeight()) {
|
|
throw IllegalArgumentException("Requested row is outside the image.");
|
|
}
|
|
int width = getWidth();
|
|
// TODO(flyashi): determine if row has enough size.
|
|
if (row == NULL) {
|
|
row = new unsigned char[width_];
|
|
}
|
|
int offset = (y + top_) * dataWidth_ + left_;
|
|
memcpy(row, &greyData_[offset], width);
|
|
return row;
|
|
}
|
|
|
|
unsigned char* GreyscaleLuminanceSource::getMatrix() {
|
|
int size = width_ * height_;
|
|
unsigned char* result = new unsigned char[size];
|
|
if (left_ == 0 && top_ == 0 && dataWidth_ == width_ && dataHeight_ == height_) {
|
|
memcpy(result, greyData_, size);
|
|
} else {
|
|
for (int row = 0; row < height_; row++) {
|
|
memcpy(result + row * width_, greyData_ + (top_ + row) * dataWidth_ + left_, width_);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Ref<LuminanceSource> GreyscaleLuminanceSource::rotateCounterClockwise() {
|
|
// Intentionally flip the left, top, width, and height arguments as needed. dataWidth and
|
|
// dataHeight are always kept unrotated.
|
|
return Ref<LuminanceSource> (new GreyscaleRotatedLuminanceSource(greyData_, dataWidth_,
|
|
dataHeight_, top_, left_, height_, width_));
|
|
}
|
|
|
|
} /* namespace */
|
|
|
|
// file: zxing/common/GreyscaleRotatedLuminanceSource.cpp
|
|
|
|
/*
|
|
* GreyscaleRotatedLuminanceSource.cpp
|
|
* zxing
|
|
*
|
|
* Copyright 2010 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/common/GreyscaleRotatedLuminanceSource.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
namespace zxing {
|
|
|
|
// Note that dataWidth and dataHeight are not reversed, as we need to be able to traverse the
|
|
// greyData correctly, which does not get rotated.
|
|
GreyscaleRotatedLuminanceSource::GreyscaleRotatedLuminanceSource(unsigned char* greyData,
|
|
int dataWidth, int dataHeight, int left, int top, int width, int height) : greyData_(greyData),
|
|
dataWidth_(dataWidth), dataHeight_(dataHeight), left_(left), top_(top), width_(width),
|
|
height_(height) {
|
|
|
|
// Intentionally comparing to the opposite dimension since we're rotated.
|
|
if (left + width > dataHeight || top + height > dataWidth) {
|
|
throw IllegalArgumentException("Crop rectangle does not fit within image data.");
|
|
}
|
|
}
|
|
|
|
// The API asks for rows, but we're rotated, so we return columns.
|
|
unsigned char* GreyscaleRotatedLuminanceSource::getRow(int y, unsigned char* row) {
|
|
if (y < 0 || y >= getHeight()) {
|
|
throw IllegalArgumentException("Requested row is outside the image");
|
|
}
|
|
int width = getWidth();
|
|
if (row == NULL) {
|
|
row = new unsigned char[width];
|
|
}
|
|
int offset = (left_ * dataWidth_) + (dataWidth_ - (y + top_));
|
|
for (int x = 0; x < width; x++) {
|
|
row[x] = greyData_[offset];
|
|
offset += dataWidth_;
|
|
}
|
|
return row;
|
|
}
|
|
|
|
unsigned char* GreyscaleRotatedLuminanceSource::getMatrix() {
|
|
unsigned char* result = new unsigned char[width_ * height_];
|
|
// This depends on getRow() honoring its second parameter.
|
|
for (int y = 0; y < height_; y++) {
|
|
getRow(y, &result[y * width_]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// file: zxing/common/GridSampler.cpp
|
|
|
|
/*
|
|
* GridSampler.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 18/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/common/GridSampler.h>
|
|
// #include <zxing/common/PerspectiveTransform.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <iostream>
|
|
// #include <sstream>
|
|
|
|
namespace zxing {
|
|
using namespace std;
|
|
|
|
GridSampler GridSampler::gridSampler;
|
|
|
|
GridSampler::GridSampler() {
|
|
}
|
|
|
|
Ref<BitMatrix> GridSampler::sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform> transform) {
|
|
Ref<BitMatrix> bits(new BitMatrix(dimension));
|
|
vector<float> points(dimension << 1, (const float)0.0f);
|
|
for (int y = 0; y < dimension; y++) {
|
|
int max = points.size();
|
|
float yValue = (float)y + 0.5f;
|
|
for (int x = 0; x < max; x += 2) {
|
|
points[x] = (float)(x >> 1) + 0.5f;
|
|
points[x + 1] = yValue;
|
|
}
|
|
transform->transformPoints(points);
|
|
checkAndNudgePoints(image, points);
|
|
for (int x = 0; x < max; x += 2) {
|
|
if (image->get((int)points[x], (int)points[x + 1])) {
|
|
bits->set(x >> 1, y);
|
|
}
|
|
}
|
|
}
|
|
return bits;
|
|
}
|
|
|
|
Ref<BitMatrix> GridSampler::sampleGrid(Ref<BitMatrix> image, int dimensionX, int dimensionY, Ref<PerspectiveTransform> transform) {
|
|
Ref<BitMatrix> bits(new BitMatrix(dimensionX, dimensionY));
|
|
vector<float> points(dimensionX << 1, (const float)0.0f);
|
|
for (int y = 0; y < dimensionY; y++) {
|
|
int max = points.size();
|
|
float yValue = (float)y + 0.5f;
|
|
for (int x = 0; x < max; x += 2) {
|
|
points[x] = (float)(x >> 1) + 0.5f;
|
|
points[x + 1] = yValue;
|
|
}
|
|
transform->transformPoints(points);
|
|
checkAndNudgePoints(image, points);
|
|
for (int x = 0; x < max; x += 2) {
|
|
if (image->get((int)points[x], (int)points[x + 1])) {
|
|
bits->set(x >> 1, y);
|
|
}
|
|
}
|
|
}
|
|
return bits;
|
|
}
|
|
|
|
Ref<BitMatrix> GridSampler::sampleGrid(Ref<BitMatrix> image, int dimension, float p1ToX, float p1ToY, float p2ToX,
|
|
float p2ToY, float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX,
|
|
float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY) {
|
|
Ref<PerspectiveTransform> transform(PerspectiveTransform::quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY,
|
|
p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY));
|
|
|
|
return sampleGrid(image, dimension, transform);
|
|
|
|
}
|
|
|
|
void GridSampler::checkAndNudgePoints(Ref<BitMatrix> image, vector<float> &points) {
|
|
int width = image->getWidth();
|
|
int height = image->getHeight();
|
|
|
|
|
|
// The Java code assumes that if the start and end points are in bounds, the rest will also be.
|
|
// However, in some unusual cases points in the middle may also be out of bounds.
|
|
// Since we can't rely on an ArrayIndexOutOfBoundsException like Java, we check every point.
|
|
|
|
for (size_t offset = 0; offset < points.size(); offset += 2) {
|
|
int x = (int)points[offset];
|
|
int y = (int)points[offset + 1];
|
|
if (x < -1 || x > width || y < -1 || y > height) {
|
|
ostringstream s;
|
|
s << "Transformed point out of bounds at " << x << "," << y;
|
|
throw ReaderException(s.str().c_str());
|
|
}
|
|
|
|
if (x == -1) {
|
|
points[offset] = 0.0f;
|
|
} else if (x == width) {
|
|
points[offset] = width - 1;
|
|
}
|
|
if (y == -1) {
|
|
points[offset + 1] = 0.0f;
|
|
} else if (y == height) {
|
|
points[offset + 1] = height - 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
GridSampler &GridSampler::getInstance() {
|
|
return gridSampler;
|
|
}
|
|
}
|
|
|
|
// file: zxing/common/HybridBinarizer.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* HybridBinarizer.cpp
|
|
* zxing
|
|
*
|
|
* Copyright 2010 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/common/HybridBinarizer.h>
|
|
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
using namespace std;
|
|
using namespace zxing;
|
|
|
|
namespace {
|
|
const int BLOCK_SIZE_POWER = 3;
|
|
const int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER;
|
|
const int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
|
|
const int MINIMUM_DIMENSION = BLOCK_SIZE * 5;
|
|
}
|
|
|
|
HybridBinarizer::HybridBinarizer(Ref<LuminanceSource> source) :
|
|
GlobalHistogramBinarizer(source), matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) {
|
|
}
|
|
|
|
HybridBinarizer::~HybridBinarizer() {
|
|
}
|
|
|
|
|
|
Ref<Binarizer>
|
|
HybridBinarizer::createBinarizer(Ref<LuminanceSource> source) {
|
|
return Ref<Binarizer> (new HybridBinarizer(source));
|
|
}
|
|
|
|
Ref<BitMatrix> HybridBinarizer::getBlackMatrix() {
|
|
// Calculates the final BitMatrix once for all requests. This could
|
|
// be called once from the constructor instead, but there are some
|
|
// advantages to doing it lazily, such as making profiling easier,
|
|
// and not doing heavy lifting when callers don't expect it.
|
|
if (matrix_) {
|
|
return matrix_;
|
|
}
|
|
LuminanceSource& source = *getLuminanceSource();
|
|
if (source.getWidth() >= MINIMUM_DIMENSION &&
|
|
source.getHeight() >= MINIMUM_DIMENSION) {
|
|
unsigned char* luminances = source.getMatrix();
|
|
int width = source.getWidth();
|
|
int height = source.getHeight();
|
|
int subWidth = width >> BLOCK_SIZE_POWER;
|
|
if ((width & BLOCK_SIZE_MASK) != 0) {
|
|
subWidth++;
|
|
}
|
|
int subHeight = height >> BLOCK_SIZE_POWER;
|
|
if ((height & BLOCK_SIZE_MASK) != 0) {
|
|
subHeight++;
|
|
}
|
|
int* blackPoints =
|
|
calculateBlackPoints(luminances, subWidth, subHeight, width, height);
|
|
|
|
Ref<BitMatrix> newMatrix (new BitMatrix(width, height));
|
|
calculateThresholdForBlock(luminances,
|
|
subWidth,
|
|
subHeight,
|
|
width,
|
|
height,
|
|
blackPoints,
|
|
newMatrix);
|
|
matrix_ = newMatrix;
|
|
|
|
// N.B.: these deletes are inadequate if anything between the new
|
|
// and this point can throw. As of this writing, it doesn't look
|
|
// like they do.
|
|
|
|
delete [] blackPoints;
|
|
delete [] luminances;
|
|
} else {
|
|
// If the image is too small, fall back to the global histogram approach.
|
|
matrix_ = GlobalHistogramBinarizer::getBlackMatrix();
|
|
}
|
|
return matrix_;
|
|
}
|
|
|
|
void
|
|
HybridBinarizer::calculateThresholdForBlock(unsigned char* luminances,
|
|
int subWidth,
|
|
int subHeight,
|
|
int width,
|
|
int height,
|
|
int blackPoints[],
|
|
Ref<BitMatrix> const& matrix) {
|
|
for (int y = 0; y < subHeight; y++) {
|
|
int yoffset = y << BLOCK_SIZE_POWER;
|
|
if (yoffset + BLOCK_SIZE >= height) {
|
|
yoffset = height - BLOCK_SIZE;
|
|
}
|
|
for (int x = 0; x < subWidth; x++) {
|
|
int xoffset = x << BLOCK_SIZE_POWER;
|
|
if (xoffset + BLOCK_SIZE >= width) {
|
|
xoffset = width - BLOCK_SIZE;
|
|
}
|
|
int left = (x > 1) ? x : 2;
|
|
left = (left < subWidth - 2) ? left : subWidth - 3;
|
|
int top = (y > 1) ? y : 2;
|
|
top = (top < subHeight - 2) ? top : subHeight - 3;
|
|
int sum = 0;
|
|
for (int z = -2; z <= 2; z++) {
|
|
int *blackRow = &blackPoints[(top + z) * subWidth];
|
|
sum += blackRow[left - 2];
|
|
sum += blackRow[left - 1];
|
|
sum += blackRow[left];
|
|
sum += blackRow[left + 1];
|
|
sum += blackRow[left + 2];
|
|
}
|
|
int average = sum / 25;
|
|
threshold8x8Block(luminances, xoffset, yoffset, average, width, matrix);
|
|
}
|
|
}
|
|
}
|
|
|
|
void HybridBinarizer::threshold8x8Block(unsigned char* luminances,
|
|
int xoffset,
|
|
int yoffset,
|
|
int threshold,
|
|
int stride,
|
|
Ref<BitMatrix> const& matrix) {
|
|
for (int y = 0, offset = yoffset * stride + xoffset;
|
|
y < BLOCK_SIZE;
|
|
y++, offset += stride) {
|
|
for (int x = 0; x < BLOCK_SIZE; x++) {
|
|
int pixel = luminances[offset + x] & 0xff;
|
|
if (pixel <= threshold) {
|
|
matrix->set(xoffset + x, yoffset + y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
inline int getBlackPointFromNeighbors(int* blackPoints, int subWidth, int x, int y) {
|
|
return (blackPoints[(y-1)*subWidth+x] +
|
|
2*blackPoints[y*subWidth+x-1] +
|
|
blackPoints[(y-1)*subWidth+x-1]) >> 2;
|
|
}
|
|
}
|
|
|
|
int* HybridBinarizer::calculateBlackPoints(unsigned char* luminances, int subWidth, int subHeight,
|
|
int width, int height) {
|
|
int *blackPoints = new int[subHeight * subWidth];
|
|
for (int y = 0; y < subHeight; y++) {
|
|
int yoffset = y << BLOCK_SIZE_POWER;
|
|
if (yoffset + BLOCK_SIZE >= height) {
|
|
yoffset = height - BLOCK_SIZE;
|
|
}
|
|
for (int x = 0; x < subWidth; x++) {
|
|
int xoffset = x << BLOCK_SIZE_POWER;
|
|
if (xoffset + BLOCK_SIZE >= width) {
|
|
xoffset = width - BLOCK_SIZE;
|
|
}
|
|
int sum = 0;
|
|
int min = 0xFF;
|
|
int max = 0;
|
|
for (int yy = 0, offset = yoffset * width + xoffset;
|
|
yy < BLOCK_SIZE;
|
|
yy++, offset += width) {
|
|
for (int xx = 0; xx < BLOCK_SIZE; xx++) {
|
|
int pixel = luminances[offset + xx] & 0xFF;
|
|
sum += pixel;
|
|
if (pixel < min) {
|
|
min = pixel;
|
|
}
|
|
if (pixel > max) {
|
|
max = pixel;
|
|
}
|
|
}
|
|
}
|
|
|
|
// See
|
|
// http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
|
|
int average = sum >> 6;
|
|
if (max - min <= 24) {
|
|
average = min >> 1;
|
|
if (y > 0 && x > 0) {
|
|
int bp = getBlackPointFromNeighbors(blackPoints, subWidth, x, y);
|
|
if (min < bp) {
|
|
average = bp;
|
|
}
|
|
}
|
|
}
|
|
blackPoints[y * subWidth + x] = average;
|
|
}
|
|
}
|
|
return blackPoints;
|
|
}
|
|
|
|
|
|
// file: zxing/common/IllegalArgumentException.cpp
|
|
|
|
/*
|
|
* IllegalArgumentException.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 06/05/2008.
|
|
* Copyright 2008 Google UK. 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/common/IllegalArgumentException.h>
|
|
|
|
namespace zxing {
|
|
|
|
IllegalArgumentException::IllegalArgumentException(const char *msg) :
|
|
Exception(msg) {
|
|
}
|
|
IllegalArgumentException::~IllegalArgumentException() throw() {
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/common/PerspectiveTransform.cpp
|
|
|
|
/*
|
|
* PerspectiveTransform.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 12/05/2008.
|
|
* Copyright 2008 Google UK. 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/common/PerspectiveTransform.h>
|
|
|
|
namespace zxing {
|
|
using namespace std;
|
|
|
|
PerspectiveTransform::PerspectiveTransform(float inA11, float inA21,
|
|
float inA31, float inA12,
|
|
float inA22, float inA32,
|
|
float inA13, float inA23,
|
|
float inA33) :
|
|
a11(inA11), a12(inA12), a13(inA13), a21(inA21), a22(inA22), a23(inA23),
|
|
a31(inA31), a32(inA32), a33(inA33) {}
|
|
|
|
Ref<PerspectiveTransform> PerspectiveTransform::quadrilateralToQuadrilateral(float x0, float y0, float x1, float y1,
|
|
float x2, float y2, float x3, float y3, float x0p, float y0p, float x1p, float y1p, float x2p, float y2p,
|
|
float x3p, float y3p) {
|
|
Ref<PerspectiveTransform> qToS = PerspectiveTransform::quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
|
|
Ref<PerspectiveTransform> sToQ =
|
|
PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
|
|
return sToQ->times(qToS);
|
|
}
|
|
|
|
Ref<PerspectiveTransform> PerspectiveTransform::squareToQuadrilateral(float x0, float y0, float x1, float y1, float x2,
|
|
float y2, float x3, float y3) {
|
|
float dy2 = y3 - y2;
|
|
float dy3 = y0 - y1 + y2 - y3;
|
|
if (dy2 == 0.0f && dy3 == 0.0f) {
|
|
Ref<PerspectiveTransform> result(new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0f,
|
|
0.0f, 1.0f));
|
|
return result;
|
|
} else {
|
|
float dx1 = x1 - x2;
|
|
float dx2 = x3 - x2;
|
|
float dx3 = x0 - x1 + x2 - x3;
|
|
float dy1 = y1 - y2;
|
|
float denominator = dx1 * dy2 - dx2 * dy1;
|
|
float a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
|
|
float a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
|
|
Ref<PerspectiveTransform> result(new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0
|
|
+ a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1.0f));
|
|
return result;
|
|
}
|
|
}
|
|
|
|
Ref<PerspectiveTransform> PerspectiveTransform::quadrilateralToSquare(float x0, float y0, float x1, float y1, float x2,
|
|
float y2, float x3, float y3) {
|
|
// Here, the adjoint serves as the inverse:
|
|
return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3)->buildAdjoint();
|
|
}
|
|
|
|
Ref<PerspectiveTransform> PerspectiveTransform::buildAdjoint() {
|
|
// Adjoint is the transpose of the cofactor matrix:
|
|
Ref<PerspectiveTransform> result(new PerspectiveTransform(a22 * a33 - a23 * a32, a23 * a31 - a21 * a33, a21 * a32
|
|
- a22 * a31, a13 * a32 - a12 * a33, a11 * a33 - a13 * a31, a12 * a31 - a11 * a32, a12 * a23 - a13 * a22,
|
|
a13 * a21 - a11 * a23, a11 * a22 - a12 * a21));
|
|
return result;
|
|
}
|
|
|
|
Ref<PerspectiveTransform> PerspectiveTransform::times(Ref<PerspectiveTransform> other) {
|
|
Ref<PerspectiveTransform> result(new PerspectiveTransform(a11 * other->a11 + a21 * other->a12 + a31 * other->a13,
|
|
a11 * other->a21 + a21 * other->a22 + a31 * other->a23, a11 * other->a31 + a21 * other->a32 + a31
|
|
* other->a33, a12 * other->a11 + a22 * other->a12 + a32 * other->a13, a12 * other->a21 + a22
|
|
* other->a22 + a32 * other->a23, a12 * other->a31 + a22 * other->a32 + a32 * other->a33, a13
|
|
* other->a11 + a23 * other->a12 + a33 * other->a13, a13 * other->a21 + a23 * other->a22 + a33
|
|
* other->a23, a13 * other->a31 + a23 * other->a32 + a33 * other->a33));
|
|
return result;
|
|
}
|
|
|
|
void PerspectiveTransform::transformPoints(vector<float> &points) {
|
|
int max = points.size();
|
|
for (int i = 0; i < max; i += 2) {
|
|
float x = points[i];
|
|
float y = points[i + 1];
|
|
float denominator = a13 * x + a23 * y + a33;
|
|
points[i] = (a11 * x + a21 * y + a31) / denominator;
|
|
points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
|
|
}
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const PerspectiveTransform &pt) {
|
|
out << pt.a11 << ", " << pt.a12 << ", " << pt.a13 << ", \n";
|
|
out << pt.a21 << ", " << pt.a22 << ", " << pt.a23 << ", \n";
|
|
out << pt.a31 << ", " << pt.a32 << ", " << pt.a33 << "\n";
|
|
return out;
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/common/Str.cpp
|
|
|
|
/*
|
|
* String.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/common/Str.h>
|
|
|
|
namespace zxing {
|
|
using namespace std;
|
|
|
|
String::String(const std::string &text) :
|
|
text_(text) {
|
|
}
|
|
const std::string& String::getText() const {
|
|
return text_;
|
|
}
|
|
|
|
ostream &operator<<(ostream &out, const String &s) {
|
|
out << s.text_;
|
|
return out;
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/common/StringUtils.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
|
|
/*
|
|
* Copyright (C) 2010-2011 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.
|
|
*/
|
|
|
|
// #include <zxing/common/StringUtils.h>
|
|
// #include <zxing/DecodeHints.h>
|
|
|
|
using namespace std;
|
|
using namespace zxing;
|
|
using namespace zxing::common;
|
|
|
|
// N.B.: these are the iconv strings for at least some versions of iconv
|
|
|
|
char const* const StringUtils::PLATFORM_DEFAULT_ENCODING = "UTF-8";
|
|
char const* const StringUtils::ASCII = "ASCII";
|
|
char const* const StringUtils::SHIFT_JIS = "SHIFT_JIS";
|
|
char const* const StringUtils::GB2312 = "GBK";
|
|
char const* const StringUtils::EUC_JP = "EUC-JP";
|
|
char const* const StringUtils::UTF8 = "UTF-8";
|
|
char const* const StringUtils::ISO88591 = "ISO8859-1";
|
|
const bool StringUtils::ASSUME_SHIFT_JIS = false;
|
|
|
|
string
|
|
StringUtils::guessEncoding(unsigned char* bytes, int length, Hashtable const& hints) {
|
|
Hashtable::const_iterator i = hints.find(DecodeHints::CHARACTER_SET);
|
|
if (i != hints.end()) {
|
|
return i->second;
|
|
}
|
|
// Does it start with the UTF-8 byte order mark? then guess it's UTF-8
|
|
if (length > 3 &&
|
|
bytes[0] == (unsigned char) 0xEF &&
|
|
bytes[1] == (unsigned char) 0xBB &&
|
|
bytes[2] == (unsigned char) 0xBF) {
|
|
return UTF8;
|
|
}
|
|
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
|
|
// which should be by far the most common encodings. ISO-8859-1
|
|
// should not have bytes in the 0x80 - 0x9F range, while Shift_JIS
|
|
// uses this as a first byte of a two-byte character. If we see this
|
|
// followed by a valid second byte in Shift_JIS, assume it is Shift_JIS.
|
|
// If we see something else in that second byte, we'll make the risky guess
|
|
// that it's UTF-8.
|
|
bool canBeISO88591 = true;
|
|
bool canBeShiftJIS = true;
|
|
bool canBeUTF8 = true;
|
|
int utf8BytesLeft = 0;
|
|
int maybeDoubleByteCount = 0;
|
|
int maybeSingleByteKatakanaCount = 0;
|
|
bool sawLatin1Supplement = false;
|
|
bool sawUTF8Start = false;
|
|
bool lastWasPossibleDoubleByteStart = false;
|
|
|
|
for (int i = 0;
|
|
i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
|
|
i++) {
|
|
|
|
int value = bytes[i] & 0xFF;
|
|
|
|
// UTF-8 stuff
|
|
if (value >= 0x80 && value <= 0xBF) {
|
|
if (utf8BytesLeft > 0) {
|
|
utf8BytesLeft--;
|
|
}
|
|
} else {
|
|
if (utf8BytesLeft > 0) {
|
|
canBeUTF8 = false;
|
|
}
|
|
if (value >= 0xC0 && value <= 0xFD) {
|
|
sawUTF8Start = true;
|
|
int valueCopy = value;
|
|
while ((valueCopy & 0x40) != 0) {
|
|
utf8BytesLeft++;
|
|
valueCopy <<= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ISO-8859-1 stuff
|
|
|
|
if ((value == 0xC2 || value == 0xC3) && i < length - 1) {
|
|
// This is really a poor hack. The slightly more exotic characters people might want to put in
|
|
// a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings
|
|
// that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF].
|
|
int nextValue = bytes[i + 1] & 0xFF;
|
|
if (nextValue <= 0xBF &&
|
|
((value == 0xC2 && nextValue >= 0xA0) || (value == 0xC3 && nextValue >= 0x80))) {
|
|
sawLatin1Supplement = true;
|
|
}
|
|
}
|
|
if (value >= 0x7F && value <= 0x9F) {
|
|
canBeISO88591 = false;
|
|
}
|
|
|
|
// Shift_JIS stuff
|
|
|
|
if (value >= 0xA1 && value <= 0xDF) {
|
|
// count the number of characters that might be a Shift_JIS single-byte Katakana character
|
|
if (!lastWasPossibleDoubleByteStart) {
|
|
maybeSingleByteKatakanaCount++;
|
|
}
|
|
}
|
|
if (!lastWasPossibleDoubleByteStart &&
|
|
((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) {
|
|
canBeShiftJIS = false;
|
|
}
|
|
if ((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF)) {
|
|
// These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid
|
|
// second byte.
|
|
if (lastWasPossibleDoubleByteStart) {
|
|
// If we just checked this and the last byte for being a valid double-byte
|
|
// char, don't check starting on this byte. If this and the last byte
|
|
// formed a valid pair, then this shouldn't be checked to see if it starts
|
|
// a double byte pair of course.
|
|
lastWasPossibleDoubleByteStart = false;
|
|
} else {
|
|
// ... otherwise do check to see if this plus the next byte form a valid
|
|
// double byte pair encoding a character.
|
|
lastWasPossibleDoubleByteStart = true;
|
|
if (i >= length - 1) {
|
|
canBeShiftJIS = false;
|
|
} else {
|
|
int nextValue = bytes[i + 1] & 0xFF;
|
|
if (nextValue < 0x40 || nextValue > 0xFC) {
|
|
canBeShiftJIS = false;
|
|
} else {
|
|
maybeDoubleByteCount++;
|
|
}
|
|
// There is some conflicting information out there about which bytes can follow which in
|
|
// double-byte Shift_JIS characters. The rule above seems to be the one that matches practice.
|
|
}
|
|
}
|
|
} else {
|
|
lastWasPossibleDoubleByteStart = false;
|
|
}
|
|
}
|
|
if (utf8BytesLeft > 0) {
|
|
canBeUTF8 = false;
|
|
}
|
|
|
|
// Easy -- if assuming Shift_JIS and no evidence it can't be, done
|
|
if (canBeShiftJIS && ASSUME_SHIFT_JIS) {
|
|
return SHIFT_JIS;
|
|
}
|
|
if (canBeUTF8 && sawUTF8Start) {
|
|
return UTF8;
|
|
}
|
|
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is:
|
|
// - If we saw
|
|
// - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or
|
|
// - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1),
|
|
// - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS
|
|
if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) {
|
|
return SHIFT_JIS;
|
|
}
|
|
// Otherwise, we default to ISO-8859-1 unless we know it can't be
|
|
if (!sawLatin1Supplement && canBeISO88591) {
|
|
return ISO88591;
|
|
}
|
|
// Otherwise, we take a wild guess with platform encoding
|
|
return PLATFORM_DEFAULT_ENCODING;
|
|
}
|
|
|
|
// file: zxing/common/detector/MonochromeRectangleDetector.cpp
|
|
|
|
/*
|
|
* MonochromeRectangleDetector.cpp
|
|
* y_wmk
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 y_wmk 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/NotFoundException.h>
|
|
// #include <zxing/common/detector/MonochromeRectangleDetector.h>
|
|
// #include <sstream>
|
|
|
|
namespace zxing {
|
|
using namespace std;
|
|
|
|
std::vector<Ref<ResultPoint> > MonochromeRectangleDetector::detect() {
|
|
int height = image_->getHeight();
|
|
int width = image_->getWidth();
|
|
int halfHeight = height >> 1;
|
|
int halfWidth = width >> 1;
|
|
int deltaY = max(1, height / (MAX_MODULES << 3));
|
|
int deltaX = max(1, width / (MAX_MODULES << 3));
|
|
|
|
int top = 0;
|
|
int bottom = height;
|
|
int left = 0;
|
|
int right = width;
|
|
Ref<ResultPoint> pointA(findCornerFromCenter(halfWidth, 0, left, right,
|
|
halfHeight, -deltaY, top, bottom, halfWidth >> 1));
|
|
top = (int) pointA->getY() - 1;;
|
|
Ref<ResultPoint> pointB(findCornerFromCenter(halfWidth, -deltaX, left, right,
|
|
halfHeight, 0, top, bottom, halfHeight >> 1));
|
|
left = (int) pointB->getX() - 1;
|
|
Ref<ResultPoint> pointC(findCornerFromCenter(halfWidth, deltaX, left, right,
|
|
halfHeight, 0, top, bottom, halfHeight >> 1));
|
|
right = (int) pointC->getX() + 1;
|
|
Ref<ResultPoint> pointD(findCornerFromCenter(halfWidth, 0, left, right,
|
|
halfHeight, deltaY, top, bottom, halfWidth >> 1));
|
|
bottom = (int) pointD->getY() + 1;
|
|
|
|
// Go try to find point A again with better information -- might have been off at first.
|
|
pointA.reset(findCornerFromCenter(halfWidth, 0, left, right,
|
|
halfHeight, -deltaY, top, bottom, halfWidth >> 2));
|
|
|
|
std::vector<Ref<ResultPoint> > corners(4);
|
|
corners[0].reset(pointA);
|
|
corners[1].reset(pointB);
|
|
corners[2].reset(pointC);
|
|
corners[3].reset(pointD);
|
|
return corners;
|
|
}
|
|
|
|
Ref<ResultPoint> MonochromeRectangleDetector::findCornerFromCenter(int centerX, int deltaX, int left, int right,
|
|
int centerY, int deltaY, int top, int bottom, int maxWhiteRun) {
|
|
Ref<TwoInts> lastRange(NULL);
|
|
for (int y = centerY, x = centerX;
|
|
y < bottom && y >= top && x < right && x >= left;
|
|
y += deltaY, x += deltaX) {
|
|
Ref<TwoInts> range(NULL);
|
|
if (deltaX == 0) {
|
|
// horizontal slices, up and down
|
|
range = blackWhiteRange(y, maxWhiteRun, left, right, true);
|
|
} else {
|
|
// vertical slices, left and right
|
|
range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);
|
|
}
|
|
if (range == NULL) {
|
|
if (lastRange == NULL) {
|
|
throw NotFoundException("Couldn't find corners (lastRange = NULL) ");
|
|
} else {
|
|
// lastRange was found
|
|
if (deltaX == 0) {
|
|
int lastY = y - deltaY;
|
|
if (lastRange->start < centerX) {
|
|
if (lastRange->end > centerX) {
|
|
// straddle, choose one or the other based on direction
|
|
Ref<ResultPoint> result(new ResultPoint(deltaY > 0 ? lastRange->start : lastRange->end, lastY));
|
|
return result;
|
|
}
|
|
Ref<ResultPoint> result(new ResultPoint(lastRange->start, lastY));
|
|
return result;
|
|
} else {
|
|
Ref<ResultPoint> result(new ResultPoint(lastRange->end, lastY));
|
|
return result;
|
|
}
|
|
} else {
|
|
int lastX = x - deltaX;
|
|
if (lastRange->start < centerY) {
|
|
if (lastRange->end > centerY) {
|
|
Ref<ResultPoint> result(new ResultPoint(lastX, deltaX < 0 ? lastRange->start : lastRange->end));
|
|
return result;
|
|
}
|
|
Ref<ResultPoint> result(new ResultPoint(lastX, lastRange->start));
|
|
return result;
|
|
} else {
|
|
Ref<ResultPoint> result(new ResultPoint(lastX, lastRange->end));
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
lastRange = range;
|
|
}
|
|
throw NotFoundException("Couldn't find corners");
|
|
}
|
|
|
|
Ref<TwoInts> MonochromeRectangleDetector::blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim,
|
|
bool horizontal) {
|
|
|
|
int center = (minDim + maxDim) >> 1;
|
|
|
|
// Scan left/up first
|
|
int start = center;
|
|
while (start >= minDim) {
|
|
if (horizontal ? image_->get(start, fixedDimension) : image_->get(fixedDimension, start)) {
|
|
start--;
|
|
} else {
|
|
int whiteRunStart = start;
|
|
do {
|
|
start--;
|
|
} while (start >= minDim && !(horizontal ? image_->get(start, fixedDimension) :
|
|
image_->get(fixedDimension, start)));
|
|
int whiteRunSize = whiteRunStart - start;
|
|
if (start < minDim || whiteRunSize > maxWhiteRun) {
|
|
start = whiteRunStart;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
start++;
|
|
|
|
// Then try right/down
|
|
int end = center;
|
|
while (end < maxDim) {
|
|
if (horizontal ? image_->get(end, fixedDimension) : image_->get(fixedDimension, end)) {
|
|
end++;
|
|
} else {
|
|
int whiteRunStart = end;
|
|
do {
|
|
end++;
|
|
} while (end < maxDim && !(horizontal ? image_->get(end, fixedDimension) :
|
|
image_->get(fixedDimension, end)));
|
|
int whiteRunSize = end - whiteRunStart;
|
|
if (end >= maxDim || whiteRunSize > maxWhiteRun) {
|
|
end = whiteRunStart;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
end--;
|
|
Ref<TwoInts> result(NULL);
|
|
if (end > start) {
|
|
result = new TwoInts;
|
|
result->start = start;
|
|
result->end = end;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// file: zxing/common/detector/WhiteRectangleDetector.cpp
|
|
|
|
/*
|
|
* WhiteRectangleDetector.cpp
|
|
* y_wmk
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 y_wmk 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/NotFoundException.h>
|
|
// #include <zxing/common/detector/WhiteRectangleDetector.h>
|
|
// #include <math.h>
|
|
// #include <sstream>
|
|
|
|
namespace zxing {
|
|
using namespace std;
|
|
|
|
int WhiteRectangleDetector::INIT_SIZE = 30;
|
|
int WhiteRectangleDetector::CORR = 1;
|
|
|
|
|
|
WhiteRectangleDetector::WhiteRectangleDetector(Ref<BitMatrix> image) : image_(image) {
|
|
width_ = image->getWidth();
|
|
height_ = image->getHeight();
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Detects a candidate barcode-like rectangular region within an image. It
|
|
* starts around the center of the image, increases the size of the candidate
|
|
* region until it finds a white rectangular region.
|
|
* </p>
|
|
*
|
|
* @return {@link vector<Ref<ResultPoint> >} describing the corners of the rectangular
|
|
* region. The first and last points are opposed on the diagonal, as
|
|
* are the second and third. The first point will be the topmost
|
|
* point and the last, the bottommost. The second point will be
|
|
* leftmost and the third, the rightmost
|
|
* @throws NotFoundException if no Data Matrix Code can be found
|
|
*/
|
|
std::vector<Ref<ResultPoint> > WhiteRectangleDetector::detect() {
|
|
int left = (width_ - INIT_SIZE) >> 1;
|
|
int right = (width_ + INIT_SIZE) >> 1;
|
|
int up = (height_ - INIT_SIZE) >> 1;
|
|
int down = (height_ + INIT_SIZE) >> 1;
|
|
if (up < 0 || left < 0 || down >= height_ || right >= width_) {
|
|
throw NotFoundException("Invalid dimensions WhiteRectangleDetector");
|
|
}
|
|
|
|
bool sizeExceeded = false;
|
|
bool aBlackPointFoundOnBorder = true;
|
|
bool atLeastOneBlackPointFoundOnBorder = false;
|
|
|
|
while (aBlackPointFoundOnBorder) {
|
|
aBlackPointFoundOnBorder = false;
|
|
|
|
// .....
|
|
// . |
|
|
// .....
|
|
bool rightBorderNotWhite = true;
|
|
while (rightBorderNotWhite && right < width_) {
|
|
rightBorderNotWhite = containsBlackPoint(up, down, right, false);
|
|
if (rightBorderNotWhite) {
|
|
right++;
|
|
aBlackPointFoundOnBorder = true;
|
|
}
|
|
}
|
|
|
|
if (right >= width_) {
|
|
sizeExceeded = true;
|
|
break;
|
|
}
|
|
|
|
// .....
|
|
// . .
|
|
// .___.
|
|
bool bottomBorderNotWhite = true;
|
|
while (bottomBorderNotWhite && down < height_) {
|
|
bottomBorderNotWhite = containsBlackPoint(left, right, down, true);
|
|
if (bottomBorderNotWhite) {
|
|
down++;
|
|
aBlackPointFoundOnBorder = true;
|
|
}
|
|
}
|
|
|
|
if (down >= height_) {
|
|
sizeExceeded = true;
|
|
break;
|
|
}
|
|
|
|
// .....
|
|
// | .
|
|
// .....
|
|
bool leftBorderNotWhite = true;
|
|
while (leftBorderNotWhite && left >= 0) {
|
|
leftBorderNotWhite = containsBlackPoint(up, down, left, false);
|
|
if (leftBorderNotWhite) {
|
|
left--;
|
|
aBlackPointFoundOnBorder = true;
|
|
}
|
|
}
|
|
|
|
if (left < 0) {
|
|
sizeExceeded = true;
|
|
break;
|
|
}
|
|
|
|
// .___.
|
|
// . .
|
|
// .....
|
|
bool topBorderNotWhite = true;
|
|
while (topBorderNotWhite && up >= 0) {
|
|
topBorderNotWhite = containsBlackPoint(left, right, up, true);
|
|
if (topBorderNotWhite) {
|
|
up--;
|
|
aBlackPointFoundOnBorder = true;
|
|
}
|
|
}
|
|
|
|
if (up < 0) {
|
|
sizeExceeded = true;
|
|
break;
|
|
}
|
|
|
|
if (aBlackPointFoundOnBorder) {
|
|
atLeastOneBlackPointFoundOnBorder = true;
|
|
}
|
|
|
|
}
|
|
if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) {
|
|
|
|
int maxSize = right - left;
|
|
|
|
Ref<ResultPoint> z(NULL);
|
|
//go up right
|
|
for (int i = 1; i < maxSize; i++) {
|
|
z = getBlackPointOnSegment(left, down - i, left + i, down);
|
|
if (z != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (z == NULL) {
|
|
throw NotFoundException("z == NULL");
|
|
}
|
|
|
|
Ref<ResultPoint> t(NULL);
|
|
//go down right
|
|
for (int i = 1; i < maxSize; i++) {
|
|
t = getBlackPointOnSegment(left, up + i, left + i, up);
|
|
if (t != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (t == NULL) {
|
|
throw NotFoundException("t == NULL");
|
|
}
|
|
|
|
Ref<ResultPoint> x(NULL);
|
|
//go down left
|
|
for (int i = 1; i < maxSize; i++) {
|
|
x = getBlackPointOnSegment(right, up + i, right - i, up);
|
|
if (x != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (x == NULL) {
|
|
throw NotFoundException("x == NULL");
|
|
}
|
|
|
|
Ref<ResultPoint> y(NULL);
|
|
//go up left
|
|
for (int i = 1; i < maxSize; i++) {
|
|
y = getBlackPointOnSegment(right, down - i, right - i, down);
|
|
if (y != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (y == NULL) {
|
|
throw NotFoundException("y == NULL");
|
|
}
|
|
|
|
return centerEdges(y, z, x, t);
|
|
|
|
} else {
|
|
throw NotFoundException("No black point found on border");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ends up being a bit faster than Math.round(). This merely rounds its
|
|
* argument to the nearest int, where x.5 rounds up.
|
|
*/
|
|
int WhiteRectangleDetector::round(float d) {
|
|
return (int) (d + 0.5f);
|
|
}
|
|
|
|
Ref<ResultPoint> WhiteRectangleDetector::getBlackPointOnSegment(float aX, float aY, float bX, float bY) {
|
|
int dist = distanceL2(aX, aY, bX, bY);
|
|
float xStep = (bX - aX) / dist;
|
|
float yStep = (bY - aY) / dist;
|
|
for (int i = 0; i < dist; i++) {
|
|
int x = round(aX + i * xStep);
|
|
int y = round(aY + i * yStep);
|
|
if (image_->get(x, y)) {
|
|
Ref<ResultPoint> point(new ResultPoint(x, y));
|
|
return point;
|
|
}
|
|
}
|
|
Ref<ResultPoint> point(NULL);
|
|
return point;
|
|
}
|
|
|
|
int WhiteRectangleDetector::distanceL2(float aX, float aY, float bX, float bY) {
|
|
float xDiff = aX - bX;
|
|
float yDiff = aY - bY;
|
|
return round((float)sqrt(xDiff * xDiff + yDiff * yDiff));
|
|
}
|
|
|
|
/**
|
|
* recenters the points of a constant distance towards the center
|
|
*
|
|
* @param y bottom most point
|
|
* @param z left most point
|
|
* @param x right most point
|
|
* @param t top most point
|
|
* @return {@link vector<Ref<ResultPoint> >} describing the corners of the rectangular
|
|
* region. The first and last points are opposed on the diagonal, as
|
|
* are the second and third. The first point will be the topmost
|
|
* point and the last, the bottommost. The second point will be
|
|
* leftmost and the third, the rightmost
|
|
*/
|
|
vector<Ref<ResultPoint> > WhiteRectangleDetector::centerEdges(Ref<ResultPoint> y, Ref<ResultPoint> z,
|
|
Ref<ResultPoint> x, Ref<ResultPoint> t) {
|
|
|
|
//
|
|
// t t
|
|
// z x
|
|
// x OR z
|
|
// y y
|
|
//
|
|
|
|
float yi = y->getX();
|
|
float yj = y->getY();
|
|
float zi = z->getX();
|
|
float zj = z->getY();
|
|
float xi = x->getX();
|
|
float xj = x->getY();
|
|
float ti = t->getX();
|
|
float tj = t->getY();
|
|
|
|
std::vector<Ref<ResultPoint> > corners(4);
|
|
if (yi < (float)width_/2) {
|
|
Ref<ResultPoint> pointA(new ResultPoint(ti - CORR, tj + CORR));
|
|
Ref<ResultPoint> pointB(new ResultPoint(zi + CORR, zj + CORR));
|
|
Ref<ResultPoint> pointC(new ResultPoint(xi - CORR, xj - CORR));
|
|
Ref<ResultPoint> pointD(new ResultPoint(yi + CORR, yj - CORR));
|
|
corners[0].reset(pointA);
|
|
corners[1].reset(pointB);
|
|
corners[2].reset(pointC);
|
|
corners[3].reset(pointD);
|
|
} else {
|
|
Ref<ResultPoint> pointA(new ResultPoint(ti + CORR, tj + CORR));
|
|
Ref<ResultPoint> pointB(new ResultPoint(zi + CORR, zj - CORR));
|
|
Ref<ResultPoint> pointC(new ResultPoint(xi - CORR, xj + CORR));
|
|
Ref<ResultPoint> pointD(new ResultPoint(yi - CORR, yj - CORR));
|
|
corners[0].reset(pointA);
|
|
corners[1].reset(pointB);
|
|
corners[2].reset(pointC);
|
|
corners[3].reset(pointD);
|
|
}
|
|
return corners;
|
|
}
|
|
|
|
/**
|
|
* Determines whether a segment contains a black point
|
|
*
|
|
* @param a min value of the scanned coordinate
|
|
* @param b max value of the scanned coordinate
|
|
* @param fixed value of fixed coordinate
|
|
* @param horizontal set to true if scan must be horizontal, false if vertical
|
|
* @return true if a black point has been found, else false.
|
|
*/
|
|
bool WhiteRectangleDetector::containsBlackPoint(int a, int b, int fixed, bool horizontal) {
|
|
if (horizontal) {
|
|
for (int x = a; x <= b; x++) {
|
|
if (image_->get(x, fixed)) {
|
|
return true;
|
|
}
|
|
}
|
|
} else {
|
|
for (int y = a; y <= b; y++) {
|
|
if (image_->get(fixed, y)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// file: zxing/common/reedsolomon/GF256.cpp
|
|
|
|
/*
|
|
* GF256.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 05/05/2008.
|
|
* Copyright 2008 Google UK. 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 <vector>
|
|
// #include <iostream>
|
|
// #include <zxing/common/reedsolomon/GF256.h>
|
|
// #include <zxing/common/reedsolomon/GF256Poly.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
// #include <zxing/common/Array.h>
|
|
// #include <zxing/common/Counted.h>
|
|
|
|
namespace zxing {
|
|
using namespace std;
|
|
|
|
static inline ArrayRef<int> makeArray(int value) {
|
|
ArrayRef<int> valuesRef(new Array<int> (value, 1));
|
|
return valuesRef;
|
|
}
|
|
|
|
static inline Ref<GF256Poly> refPoly(GF256 &field, int value) {
|
|
ArrayRef<int> values(makeArray(value));
|
|
Ref<GF256Poly> result(new GF256Poly(field, values));
|
|
return result;
|
|
}
|
|
|
|
GF256::GF256(int primitive) :
|
|
exp_(256, (const int)0), log_(256, (const int)0), zero_(refPoly(*this, 0)), one_(refPoly(*this, 1)) {
|
|
int x = 1;
|
|
for (int i = 0; i < 256; i++) {
|
|
exp_[i] = x;
|
|
x <<= 1;
|
|
if (x >= 0x100) {
|
|
x ^= primitive;
|
|
}
|
|
}
|
|
|
|
// log(0) == 0, but should never be used
|
|
log_[0] = 0;
|
|
for (int i = 0; i < 255; i++) {
|
|
log_[exp_[i]] = i;
|
|
}
|
|
}
|
|
|
|
Ref<GF256Poly> GF256::getZero() {
|
|
return zero_;
|
|
}
|
|
|
|
Ref<GF256Poly> GF256::getOne() {
|
|
return one_;
|
|
}
|
|
|
|
Ref<GF256Poly> GF256::buildMonomial(int degree, int coefficient) {
|
|
#ifdef DEBUG
|
|
cout << __FUNCTION__ << "\n";
|
|
#endif
|
|
if (degree < 0) {
|
|
throw IllegalArgumentException("Degree must be non-negative");
|
|
}
|
|
if (coefficient == 0) {
|
|
return zero_;
|
|
}
|
|
int nCoefficients = degree + 1;
|
|
ArrayRef<int> coefficients(new Array<int> (nCoefficients));
|
|
coefficients[0] = coefficient;
|
|
Ref<GF256Poly> result(new GF256Poly(*this, coefficients));
|
|
return result;
|
|
}
|
|
|
|
int GF256::addOrSubtract(int a, int b) {
|
|
return a ^ b;
|
|
}
|
|
|
|
int GF256::exp(int a) {
|
|
return exp_[a];
|
|
}
|
|
|
|
int GF256::log(int a) {
|
|
if (a == 0) {
|
|
throw IllegalArgumentException("Cannot take the logarithm of 0");
|
|
}
|
|
return log_[a];
|
|
}
|
|
|
|
int GF256::inverse(int a) {
|
|
if (a == 0) {
|
|
throw IllegalArgumentException("Cannot calculate the inverse of 0");
|
|
}
|
|
return exp_[255 - log_[a]];
|
|
}
|
|
|
|
int GF256::multiply(int a, int b) {
|
|
if (a == 0 || b == 0) {
|
|
return 0;
|
|
}
|
|
int logSum = log_[a] + log_[b];
|
|
// index is a sped-up alternative to logSum % 255 since sum
|
|
// is in [0,510]. Thanks to jmsachs for the idea
|
|
return exp_[(logSum & 0xFF) + (logSum >> 8)];
|
|
}
|
|
|
|
GF256 GF256::QR_CODE_FIELD(0x011D); // x^8 + x^4 + x^3 + x^2 + 1
|
|
GF256 GF256::DATA_MATRIX_FIELD(0x012D); // x^8 + x^5 + x^3 + x^2 + 1
|
|
|
|
ostream& operator<<(ostream& out, const GF256& field) {
|
|
out << "Field[\nexp=(";
|
|
out << field.exp_[0];
|
|
for (int i = 1; i < 256; i++) {
|
|
out << "," << field.exp_[i];
|
|
}
|
|
out << "),\nlog=(";
|
|
out << field.log_[0];
|
|
for (int i = 1; i < 256; i++) {
|
|
out << "," << field.log_[i];
|
|
}
|
|
out << ")\n]";
|
|
return out;
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/common/reedsolomon/GF256Poly.cpp
|
|
|
|
/*
|
|
* GF256Poly.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 05/05/2008.
|
|
* Copyright 2008 Google UK. 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 <iostream>
|
|
// #include <sstream>
|
|
// #include <zxing/common/reedsolomon/GF256Poly.h>
|
|
// #include <zxing/common/reedsolomon/GF256.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
namespace zxing {
|
|
using namespace std;
|
|
|
|
void GF256Poly::fixCoefficients() {
|
|
int coefficientsLength = coefficients.size();
|
|
if (coefficientsLength > 1 && coefficients[0] == 0) {
|
|
// Leading term must be non-zero for anything except
|
|
// the constant polynomial "0"
|
|
int firstNonZero = 1;
|
|
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
|
|
firstNonZero++;
|
|
}
|
|
if (firstNonZero == coefficientsLength) {
|
|
coefficientsLength = field.getZero()->coefficients.size();
|
|
coefficients.reset(new Array<int> (coefficientsLength));
|
|
*coefficients = *(field.getZero()->coefficients);
|
|
} else {
|
|
ArrayRef<int> c(coefficients);
|
|
coefficientsLength -= firstNonZero;
|
|
coefficients.reset(new Array<int> (coefficientsLength));
|
|
for (int i = 0; i < coefficientsLength; i++) {
|
|
coefficients[i] = c[i + firstNonZero];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GF256Poly::GF256Poly(GF256 &f, ArrayRef<int> c) :
|
|
Counted(), field(f), coefficients(c) {
|
|
fixCoefficients();
|
|
}
|
|
|
|
GF256Poly::~GF256Poly() {
|
|
|
|
}
|
|
|
|
int GF256Poly::getDegree() {
|
|
return coefficients.size() - 1;
|
|
}
|
|
|
|
bool GF256Poly::isZero() {
|
|
return coefficients[0] == 0;
|
|
}
|
|
|
|
int GF256Poly::getCoefficient(int degree) {
|
|
return coefficients[coefficients.size() - 1 - degree];
|
|
}
|
|
|
|
int GF256Poly::evaluateAt(int a) {
|
|
if (a == 0) {
|
|
return getCoefficient(0);
|
|
}
|
|
int size = coefficients.size();
|
|
if (a == 1) {
|
|
// Just the sum of the coefficients
|
|
int result = 0;
|
|
for (int i = 0; i < size; i++) {
|
|
result = GF256::addOrSubtract(result, coefficients[i]);
|
|
}
|
|
return result;
|
|
}
|
|
int result = coefficients[0];
|
|
for (int i = 1; i < size; i++) {
|
|
result = GF256::addOrSubtract(field.multiply(a, result), coefficients[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Ref<GF256Poly> GF256Poly::addOrSubtract(Ref<GF256Poly> b) {
|
|
if (&field != &b->field) {
|
|
throw IllegalArgumentException("Fields must be the same");
|
|
}
|
|
if (isZero()) {
|
|
return b;
|
|
}
|
|
if (b->isZero()) {
|
|
return Ref<GF256Poly>(this);
|
|
}
|
|
|
|
ArrayRef<int> largerCoefficients = coefficients;
|
|
ArrayRef<int> smallerCoefficients = b->coefficients;
|
|
if (smallerCoefficients.size() > largerCoefficients.size()) {
|
|
ArrayRef<int> tmp(smallerCoefficients);
|
|
smallerCoefficients = largerCoefficients;
|
|
largerCoefficients = tmp;
|
|
}
|
|
|
|
ArrayRef<int> sumDiff(new Array<int> (largerCoefficients.size()));
|
|
|
|
unsigned lengthDiff = largerCoefficients.size() - smallerCoefficients.size();
|
|
for (unsigned i = 0; i < lengthDiff; i++) {
|
|
sumDiff[i] = largerCoefficients[i];
|
|
}
|
|
for (unsigned i = lengthDiff; i < largerCoefficients.size(); i++) {
|
|
sumDiff[i] = GF256::addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
|
|
}
|
|
return Ref<GF256Poly>(new GF256Poly(field, sumDiff));
|
|
}
|
|
|
|
Ref<GF256Poly> GF256Poly::multiply(Ref<GF256Poly> b) {
|
|
if (&field != &b->field) {
|
|
throw IllegalArgumentException("Fields must be the same");
|
|
}
|
|
if (isZero() || b->isZero()) {
|
|
return field.getZero();
|
|
}
|
|
ArrayRef<int> aCoefficients = coefficients;
|
|
int aLength = aCoefficients.size();
|
|
ArrayRef<int> bCoefficients = b->coefficients;
|
|
int bLength = bCoefficients.size();
|
|
int productLength = aLength + bLength - 1;
|
|
ArrayRef<int> product(new Array<int> (productLength));
|
|
for (int i = 0; i < aLength; i++) {
|
|
int aCoeff = aCoefficients[i];
|
|
for (int j = 0; j < bLength; j++) {
|
|
product[i + j] = GF256::addOrSubtract(product[i + j], field.multiply(aCoeff, bCoefficients[j]));
|
|
}
|
|
}
|
|
|
|
return Ref<GF256Poly>(new GF256Poly(field, product));
|
|
}
|
|
|
|
Ref<GF256Poly> GF256Poly::multiply(int scalar) {
|
|
if (scalar == 0) {
|
|
return field.getZero();
|
|
}
|
|
if (scalar == 1) {
|
|
return Ref<GF256Poly>(this);
|
|
}
|
|
int size = coefficients.size();
|
|
ArrayRef<int> product(new Array<int> (size));
|
|
for (int i = 0; i < size; i++) {
|
|
product[i] = field.multiply(coefficients[i], scalar);
|
|
}
|
|
return Ref<GF256Poly>(new GF256Poly(field, product));
|
|
}
|
|
|
|
Ref<GF256Poly> GF256Poly::multiplyByMonomial(int degree, int coefficient) {
|
|
if (degree < 0) {
|
|
throw IllegalArgumentException("Degree must be non-negative");
|
|
}
|
|
if (coefficient == 0) {
|
|
return field.getZero();
|
|
}
|
|
int size = coefficients.size();
|
|
ArrayRef<int> product(new Array<int> (size + degree));
|
|
for (int i = 0; i < size; i++) {
|
|
product[i] = field.multiply(coefficients[i], coefficient);
|
|
}
|
|
return Ref<GF256Poly>(new GF256Poly(field, product));
|
|
}
|
|
|
|
const char *GF256Poly::description() const {
|
|
ostringstream result;
|
|
result << *this;
|
|
return result.str().c_str();
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const GF256Poly& p) {
|
|
GF256Poly &poly = const_cast<GF256Poly&>(p);
|
|
out << "Poly[" << poly.coefficients.size() << "]";
|
|
if (poly.coefficients.size() > 0) {
|
|
out << "(" << poly.coefficients[0];
|
|
for (unsigned i = 1; i < poly.coefficients.size(); i++) {
|
|
out << "," << poly.coefficients[i];
|
|
}
|
|
out << ")";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/common/reedsolomon/ReedSolomonDecoder.cpp
|
|
|
|
/*
|
|
* ReedSolomonDecoder.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 05/05/2008.
|
|
* Copyright 2008 Google UK. 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 <iostream>
|
|
|
|
// #include <memory>
|
|
// #include <zxing/common/reedsolomon/ReedSolomonDecoder.h>
|
|
// #include <zxing/common/reedsolomon/GF256.h>
|
|
// #include <zxing/common/reedsolomon/GF256Poly.h>
|
|
// #include <zxing/common/reedsolomon/ReedSolomonException.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
using namespace std;
|
|
|
|
namespace zxing {
|
|
|
|
ReedSolomonDecoder::ReedSolomonDecoder(GF256 &fld) :
|
|
field(fld) {
|
|
}
|
|
|
|
ReedSolomonDecoder::~ReedSolomonDecoder() {
|
|
}
|
|
|
|
void ReedSolomonDecoder::decode(ArrayRef<int> received, int twoS) {
|
|
|
|
Ref<GF256Poly> poly(new GF256Poly(field, received));
|
|
|
|
|
|
#ifdef DEBUG
|
|
cout << "decoding with poly " << *poly << "\n";
|
|
#endif
|
|
|
|
ArrayRef<int> syndromeCoefficients(new Array<int> (twoS));
|
|
|
|
|
|
#ifdef DEBUG
|
|
cout << "syndromeCoefficients array = " <<
|
|
syndromeCoefficients.array_ << "\n";
|
|
#endif
|
|
|
|
bool dataMatrix = (&field == &GF256::DATA_MATRIX_FIELD);
|
|
bool noError = true;
|
|
for (int i = 0; i < twoS; i++) {
|
|
int eval = poly->evaluateAt(field.exp(dataMatrix ? i + 1 : i));
|
|
syndromeCoefficients[syndromeCoefficients->size() - 1 - i] = eval;
|
|
if (eval != 0) {
|
|
noError = false;
|
|
}
|
|
}
|
|
if (noError) {
|
|
return;
|
|
}
|
|
|
|
Ref<GF256Poly> syndrome(new GF256Poly(field, syndromeCoefficients));
|
|
Ref<GF256Poly> monomial(field.buildMonomial(twoS, 1));
|
|
vector<Ref<GF256Poly> > sigmaOmega(runEuclideanAlgorithm(monomial, syndrome, twoS));
|
|
ArrayRef<int> errorLocations = findErrorLocations(sigmaOmega[0]);
|
|
ArrayRef<int> errorMagitudes = findErrorMagnitudes(sigmaOmega[1], errorLocations, dataMatrix);
|
|
for (unsigned i = 0; i < errorLocations->size(); i++) {
|
|
int position = received->size() - 1 - field.log(errorLocations[i]);
|
|
//TODO: check why the position would be invalid
|
|
if (position < 0 || (size_t)position >= received.size())
|
|
throw IllegalArgumentException("Invalid position (ReedSolomonDecoder)");
|
|
received[position] = GF256::addOrSubtract(received[position], errorMagitudes[i]);
|
|
}
|
|
}
|
|
|
|
vector<Ref<GF256Poly> > ReedSolomonDecoder::runEuclideanAlgorithm(Ref<GF256Poly> a, Ref<GF256Poly> b, int R) {
|
|
// Assume a's degree is >= b's
|
|
if (a->getDegree() < b->getDegree()) {
|
|
Ref<GF256Poly> tmp = a;
|
|
a = b;
|
|
b = tmp;
|
|
}
|
|
|
|
Ref<GF256Poly> rLast(a);
|
|
Ref<GF256Poly> r(b);
|
|
Ref<GF256Poly> sLast(field.getOne());
|
|
Ref<GF256Poly> s(field.getZero());
|
|
Ref<GF256Poly> tLast(field.getZero());
|
|
Ref<GF256Poly> t(field.getOne());
|
|
|
|
|
|
// Run Euclidean algorithm until r's degree is less than R/2
|
|
while (r->getDegree() >= R / 2) {
|
|
Ref<GF256Poly> rLastLast(rLast);
|
|
Ref<GF256Poly> sLastLast(sLast);
|
|
Ref<GF256Poly> tLastLast(tLast);
|
|
rLast = r;
|
|
sLast = s;
|
|
tLast = t;
|
|
|
|
|
|
// Divide rLastLast by rLast, with quotient q and remainder r
|
|
if (rLast->isZero()) {
|
|
// Oops, Euclidean algorithm already terminated?
|
|
throw ReedSolomonException("r_{i-1} was zero");
|
|
}
|
|
r = rLastLast;
|
|
Ref<GF256Poly> q(field.getZero());
|
|
int denominatorLeadingTerm = rLast->getCoefficient(rLast->getDegree());
|
|
int dltInverse = field.inverse(denominatorLeadingTerm);
|
|
while (r->getDegree() >= rLast->getDegree() && !r->isZero()) {
|
|
int degreeDiff = r->getDegree() - rLast->getDegree();
|
|
int scale = field.multiply(r->getCoefficient(r->getDegree()), dltInverse);
|
|
q = q->addOrSubtract(field.buildMonomial(degreeDiff, scale));
|
|
r = r->addOrSubtract(rLast->multiplyByMonomial(degreeDiff, scale));
|
|
}
|
|
|
|
s = q->multiply(sLast)->addOrSubtract(sLastLast);
|
|
t = q->multiply(tLast)->addOrSubtract(tLastLast);
|
|
}
|
|
|
|
int sigmaTildeAtZero = t->getCoefficient(0);
|
|
if (sigmaTildeAtZero == 0) {
|
|
throw ReedSolomonException("sigmaTilde(0) was zero");
|
|
}
|
|
|
|
int inverse = field.inverse(sigmaTildeAtZero);
|
|
Ref<GF256Poly> sigma(t->multiply(inverse));
|
|
Ref<GF256Poly> omega(r->multiply(inverse));
|
|
|
|
|
|
#ifdef DEBUG
|
|
cout << "t = " << *t << "\n";
|
|
cout << "r = " << *r << "\n";
|
|
cout << "sigma = " << *sigma << "\n";
|
|
cout << "omega = " << *omega << "\n";
|
|
#endif
|
|
|
|
vector<Ref<GF256Poly> > result(2);
|
|
result[0] = sigma;
|
|
result[1] = omega;
|
|
return result;
|
|
}
|
|
|
|
ArrayRef<int> ReedSolomonDecoder::findErrorLocations(Ref<GF256Poly> errorLocator) {
|
|
// This is a direct application of Chien's search
|
|
int numErrors = errorLocator->getDegree();
|
|
if (numErrors == 1) { // shortcut
|
|
ArrayRef<int> result(1);
|
|
result[0] = errorLocator->getCoefficient(1);
|
|
return result;
|
|
}
|
|
ArrayRef<int> result(numErrors);
|
|
int e = 0;
|
|
for (int i = 1; i < 256 && e < numErrors; i++) {
|
|
// cout << "errorLocator(" << i << ") == " << errorLocator->evaluateAt(i) << "\n";
|
|
if (errorLocator->evaluateAt(i) == 0) {
|
|
result[e] = field.inverse(i);
|
|
e++;
|
|
}
|
|
}
|
|
if (e != numErrors) {
|
|
throw ReedSolomonException("Error locator degree does not match number of roots");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ArrayRef<int> ReedSolomonDecoder::findErrorMagnitudes(Ref<GF256Poly> errorEvaluator, ArrayRef<int> errorLocations, bool dataMatrix) {
|
|
// This is directly applying Forney's Formula
|
|
int s = errorLocations.size();
|
|
ArrayRef<int> result(s);
|
|
for (int i = 0; i < s; i++) {
|
|
int xiInverse = field.inverse(errorLocations[i]);
|
|
int denominator = 1;
|
|
for (int j = 0; j < s; j++) {
|
|
if (i != j) {
|
|
denominator = field.multiply(denominator, GF256::addOrSubtract(1, field.multiply(errorLocations[j],
|
|
xiInverse)));
|
|
}
|
|
}
|
|
result[i] = field.multiply(errorEvaluator->evaluateAt(xiInverse), field.inverse(denominator));
|
|
|
|
if (dataMatrix) {
|
|
result[i] = field.multiply(result[i], xiInverse);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// file: zxing/common/reedsolomon/ReedSolomonException.cpp
|
|
|
|
/*
|
|
* ReedSolomonException.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 06/05/2008.
|
|
* Copyright 2008 Google UK. 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/common/reedsolomon/ReedSolomonException.h>
|
|
|
|
namespace zxing {
|
|
ReedSolomonException::ReedSolomonException(const char *msg) throw() :
|
|
Exception(msg) {
|
|
}
|
|
ReedSolomonException::~ReedSolomonException() throw() {
|
|
}
|
|
|
|
}
|
|
|
|
// file: zxing/datamatrix/DataMatrixReader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* DataMatrixReader.cpp
|
|
* zxing
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/datamatrix/DataMatrixReader.h>
|
|
// #include <zxing/datamatrix/detector/Detector.h>
|
|
// #include <iostream>
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
|
|
using namespace std;
|
|
|
|
DataMatrixReader::DataMatrixReader() :
|
|
decoder_() {
|
|
}
|
|
|
|
Ref<Result> DataMatrixReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
|
(void)hints;
|
|
#ifdef DEBUG
|
|
cout << "decoding image " << image.object_ << ":\n" << flush;
|
|
#endif
|
|
|
|
Detector detector(image->getBlackMatrix());
|
|
|
|
|
|
#ifdef DEBUG
|
|
cout << "(1) created detector " << &detector << "\n" << flush;
|
|
#endif
|
|
|
|
Ref<DetectorResult> detectorResult(detector.detect());
|
|
#ifdef DEBUG
|
|
cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush;
|
|
#endif
|
|
|
|
std::vector<Ref<ResultPoint> > points(detectorResult->getPoints());
|
|
|
|
|
|
#ifdef DEBUG
|
|
cout << "(3) extracted points " << &points << "\n" << flush;
|
|
cout << "found " << points.size() << " points:\n";
|
|
for (size_t i = 0; i < points.size(); i++) {
|
|
cout << " " << points[i]->getX() << "," << points[i]->getY() << "\n";
|
|
}
|
|
cout << "bits:\n";
|
|
cout << *(detectorResult->getBits()) << "\n";
|
|
#endif
|
|
|
|
Ref<DecoderResult> decoderResult(decoder_.decode(detectorResult->getBits()));
|
|
#ifdef DEBUG
|
|
cout << "(4) decoded, have decoderResult " << decoderResult.object_ << "\n" << flush;
|
|
#endif
|
|
|
|
Ref<Result> result(
|
|
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_DATA_MATRIX));
|
|
#ifdef DEBUG
|
|
cout << "(5) created result " << result.object_ << ", returning\n" << flush;
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
DataMatrixReader::~DataMatrixReader() {
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/datamatrix/Version.cpp
|
|
|
|
/*
|
|
* Version.cpp
|
|
* zxing
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/datamatrix/Version.h>
|
|
// #include <limits>
|
|
// #include <iostream>
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
using namespace std;
|
|
|
|
ECB::ECB(int count, int dataCodewords) :
|
|
count_(count), dataCodewords_(dataCodewords) {
|
|
}
|
|
|
|
int ECB::getCount() {
|
|
return count_;
|
|
}
|
|
|
|
int ECB::getDataCodewords() {
|
|
return dataCodewords_;
|
|
}
|
|
|
|
ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks) :
|
|
ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks) {
|
|
}
|
|
|
|
ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks1, ECB *ecBlocks2) :
|
|
ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks1) {
|
|
ecBlocks_.push_back(ecBlocks2);
|
|
}
|
|
|
|
int ECBlocks::getECCodewords() {
|
|
return ecCodewords_;
|
|
}
|
|
|
|
std::vector<ECB*>& ECBlocks::getECBlocks() {
|
|
return ecBlocks_;
|
|
}
|
|
|
|
ECBlocks::~ECBlocks() {
|
|
for (size_t i = 0; i < ecBlocks_.size(); i++) {
|
|
delete ecBlocks_[i];
|
|
}
|
|
}
|
|
|
|
vector<Ref<Version> > Version::VERSIONS;
|
|
static int N_VERSIONS = Version::buildVersions();
|
|
|
|
Version::Version(int versionNumber, int symbolSizeRows, int symbolSizeColumns, int dataRegionSizeRows,
|
|
int dataRegionSizeColumns, ECBlocks* ecBlocks) : versionNumber_(versionNumber),
|
|
symbolSizeRows_(symbolSizeRows), symbolSizeColumns_(symbolSizeColumns),
|
|
dataRegionSizeRows_(dataRegionSizeRows), dataRegionSizeColumns_(dataRegionSizeColumns),
|
|
ecBlocks_(ecBlocks), totalCodewords_(0) {
|
|
// Calculate the total number of codewords
|
|
int total = 0;
|
|
int ecCodewords = ecBlocks_->getECCodewords();
|
|
vector<ECB*> &ecbArray = ecBlocks_->getECBlocks();
|
|
for (unsigned int i = 0; i < ecbArray.size(); i++) {
|
|
ECB *ecBlock = ecbArray[i];
|
|
total += ecBlock->getCount() * (ecBlock->getDataCodewords() + ecCodewords);
|
|
}
|
|
totalCodewords_ = total;
|
|
}
|
|
|
|
Version::~Version() {
|
|
delete ecBlocks_;
|
|
}
|
|
|
|
int Version::getVersionNumber() {
|
|
return versionNumber_;
|
|
}
|
|
|
|
int Version::getSymbolSizeRows() {
|
|
return symbolSizeRows_;
|
|
}
|
|
|
|
int Version::getSymbolSizeColumns() {
|
|
return symbolSizeColumns_;
|
|
}
|
|
|
|
int Version::getDataRegionSizeRows() {
|
|
return dataRegionSizeRows_;
|
|
}
|
|
|
|
int Version::getDataRegionSizeColumns() {
|
|
return dataRegionSizeColumns_;
|
|
}
|
|
|
|
int Version::getTotalCodewords() {
|
|
return totalCodewords_;
|
|
}
|
|
|
|
ECBlocks* Version::getECBlocks() {
|
|
return ecBlocks_;
|
|
}
|
|
|
|
Ref<Version> Version::getVersionForDimensions(int numRows, int numColumns) {
|
|
if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {
|
|
throw ReaderException("Number of rows and columns must be even");
|
|
}
|
|
|
|
// TODO(bbrown): This is doing a linear search through the array of versions.
|
|
// If we interleave the rectangular versions with the square versions we could
|
|
// do a binary search.
|
|
for (int i = 0; i < N_VERSIONS; ++i){
|
|
Ref<Version> version(VERSIONS[i]);
|
|
if (version->getSymbolSizeRows() == numRows && version->getSymbolSizeColumns() == numColumns) {
|
|
return version;
|
|
}
|
|
}
|
|
throw ReaderException("Error version not found");
|
|
}
|
|
|
|
/**
|
|
* See ISO 16022:2006 5.5.1 Table 7
|
|
*/
|
|
int Version::buildVersions() {
|
|
VERSIONS.push_back(Ref<Version>(new Version(1, 10, 10, 8, 8,
|
|
new ECBlocks(5, new ECB(1, 3)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(2, 12, 12, 10, 10,
|
|
new ECBlocks(7, new ECB(1, 5)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(3, 14, 14, 12, 12,
|
|
new ECBlocks(10, new ECB(1, 8)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(4, 16, 16, 14, 14,
|
|
new ECBlocks(12, new ECB(1, 12)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(5, 18, 18, 16, 16,
|
|
new ECBlocks(14, new ECB(1, 18)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(6, 20, 20, 18, 18,
|
|
new ECBlocks(18, new ECB(1, 22)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(7, 22, 22, 20, 20,
|
|
new ECBlocks(20, new ECB(1, 30)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(8, 24, 24, 22, 22,
|
|
new ECBlocks(24, new ECB(1, 36)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(9, 26, 26, 24, 24,
|
|
new ECBlocks(28, new ECB(1, 44)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(10, 32, 32, 14, 14,
|
|
new ECBlocks(36, new ECB(1, 62)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(11, 36, 36, 16, 16,
|
|
new ECBlocks(42, new ECB(1, 86)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(12, 40, 40, 18, 18,
|
|
new ECBlocks(48, new ECB(1, 114)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(13, 44, 44, 20, 20,
|
|
new ECBlocks(56, new ECB(1, 144)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(14, 48, 48, 22, 22,
|
|
new ECBlocks(68, new ECB(1, 174)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(15, 52, 52, 24, 24,
|
|
new ECBlocks(42, new ECB(2, 102)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(16, 64, 64, 14, 14,
|
|
new ECBlocks(56, new ECB(2, 140)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(17, 72, 72, 16, 16,
|
|
new ECBlocks(36, new ECB(4, 92)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(18, 80, 80, 18, 18,
|
|
new ECBlocks(48, new ECB(4, 114)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(19, 88, 88, 20, 20,
|
|
new ECBlocks(56, new ECB(4, 144)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(20, 96, 96, 22, 22,
|
|
new ECBlocks(68, new ECB(4, 174)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(21, 104, 104, 24, 24,
|
|
new ECBlocks(56, new ECB(6, 136)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(22, 120, 120, 18, 18,
|
|
new ECBlocks(68, new ECB(6, 175)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(23, 132, 132, 20, 20,
|
|
new ECBlocks(62, new ECB(8, 163)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(24, 144, 144, 22, 22,
|
|
new ECBlocks(62, new ECB(8, 156), new ECB(2, 155)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(25, 8, 18, 6, 16,
|
|
new ECBlocks(7, new ECB(1, 5)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(26, 8, 32, 6, 14,
|
|
new ECBlocks(11, new ECB(1, 10)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(27, 12, 26, 10, 24,
|
|
new ECBlocks(14, new ECB(1, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(28, 12, 36, 10, 16,
|
|
new ECBlocks(18, new ECB(1, 22)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(29, 16, 36, 14, 16,
|
|
new ECBlocks(24, new ECB(1, 32)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(30, 16, 48, 14, 22,
|
|
new ECBlocks(28, new ECB(1, 49)))));
|
|
return VERSIONS.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/datamatrix/decoder/BitMatrixParser.cpp
|
|
|
|
/*
|
|
* BitMatrixParser.cpp
|
|
* zxing
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/datamatrix/decoder/BitMatrixParser.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
// #include <iostream>
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
|
|
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_(NULL),
|
|
parsedVersion_(NULL),
|
|
readBitMatrix_(NULL) {
|
|
size_t dimension = bitMatrix->getDimension();
|
|
if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0)
|
|
throw ReaderException("Dimension must be even, > 8 < 144");
|
|
|
|
parsedVersion_ = readVersion(bitMatrix);
|
|
bitMatrix_ = extractDataRegion(bitMatrix);
|
|
readBitMatrix_ = new BitMatrix(bitMatrix_->getWidth(), bitMatrix_->getHeight());
|
|
}
|
|
|
|
Ref<Version> BitMatrixParser::readVersion(Ref<BitMatrix> bitMatrix) {
|
|
if (parsedVersion_ != 0) {
|
|
return parsedVersion_;
|
|
}
|
|
|
|
int numRows = bitMatrix->getHeight();//getDimension();
|
|
int numColumns = bitMatrix->getWidth();//numRows;
|
|
|
|
Ref<Version> version = parsedVersion_->getVersionForDimensions(numRows, numColumns);
|
|
if (version != 0) {
|
|
return version;
|
|
}
|
|
throw ReaderException("Couldn't decode version");
|
|
}
|
|
|
|
ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
|
|
ArrayRef<unsigned char> result(parsedVersion_->getTotalCodewords());
|
|
int resultOffset = 0;
|
|
int row = 4;
|
|
int column = 0;
|
|
|
|
int numRows = bitMatrix_->getHeight();
|
|
int numColumns = bitMatrix_->getWidth();
|
|
|
|
bool corner1Read = false;
|
|
bool corner2Read = false;
|
|
bool corner3Read = false;
|
|
bool corner4Read = false;
|
|
|
|
// Read all of the codewords
|
|
do {
|
|
// Check the four corner cases
|
|
if ((row == numRows) && (column == 0) && !corner1Read) {
|
|
result[resultOffset++] = (unsigned char) readCorner1(numRows, numColumns);
|
|
row -= 2;
|
|
column +=2;
|
|
corner1Read = true;
|
|
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) {
|
|
result[resultOffset++] = (unsigned char) readCorner2(numRows, numColumns);
|
|
row -= 2;
|
|
column +=2;
|
|
corner2Read = true;
|
|
} else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) {
|
|
result[resultOffset++] = (unsigned char) readCorner3(numRows, numColumns);
|
|
row -= 2;
|
|
column +=2;
|
|
corner3Read = true;
|
|
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) {
|
|
result[resultOffset++] = (unsigned char) readCorner4(numRows, numColumns);
|
|
row -= 2;
|
|
column +=2;
|
|
corner4Read = true;
|
|
} else {
|
|
// Sweep upward diagonally to the right
|
|
do {
|
|
if ((row < numRows) && (column >= 0) && !readBitMatrix_->get(column, row)) {
|
|
result[resultOffset++] = (unsigned char) readUtah(row, column, numRows, numColumns);
|
|
}
|
|
row -= 2;
|
|
column +=2;
|
|
} while ((row >= 0) && (column < numColumns));
|
|
row += 1;
|
|
column +=3;
|
|
|
|
// Sweep downward diagonally to the left
|
|
do {
|
|
if ((row >= 0) && (column < numColumns) && !readBitMatrix_->get(column, row)) {
|
|
result[resultOffset++] = (unsigned char) readUtah(row, column, numRows, numColumns);
|
|
}
|
|
row += 2;
|
|
column -=2;
|
|
} while ((row < numRows) && (column >= 0));
|
|
row += 3;
|
|
column +=1;
|
|
}
|
|
} while ((row < numRows) || (column < numColumns));
|
|
|
|
if (resultOffset != parsedVersion_->getTotalCodewords()) {
|
|
throw ReaderException("Did not read all codewords");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool BitMatrixParser::readModule(int row, int column, int numRows, int numColumns) {
|
|
// Adjust the row and column indices based on boundary wrapping
|
|
if (row < 0) {
|
|
row += numRows;
|
|
column += 4 - ((numRows + 4) & 0x07);
|
|
}
|
|
if (column < 0) {
|
|
column += numColumns;
|
|
row += 4 - ((numColumns + 4) & 0x07);
|
|
}
|
|
readBitMatrix_->set(column, row);
|
|
return bitMatrix_->get(column, row);
|
|
}
|
|
|
|
int BitMatrixParser::readUtah(int row, int column, int numRows, int numColumns) {
|
|
int currentByte = 0;
|
|
if (readModule(row - 2, column - 2, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(row - 2, column - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(row - 1, column - 2, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(row - 1, column - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(row - 1, column, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(row, column - 2, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(row, column - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(row, column, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
return currentByte;
|
|
}
|
|
|
|
int BitMatrixParser::readCorner1(int numRows, int numColumns) {
|
|
int currentByte = 0;
|
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(numRows - 1, 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(numRows - 1, 2, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(2, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(3, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
return currentByte;
|
|
}
|
|
|
|
int BitMatrixParser::readCorner2(int numRows, int numColumns) {
|
|
int currentByte = 0;
|
|
if (readModule(numRows - 3, 0, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(numRows - 2, 0, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 4, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 3, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
return currentByte;
|
|
}
|
|
|
|
int BitMatrixParser::readCorner3(int numRows, int numColumns) {
|
|
int currentByte = 0;
|
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 3, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(1, numColumns - 3, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(1, numColumns - 2, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
return currentByte;
|
|
}
|
|
|
|
int BitMatrixParser::readCorner4(int numRows, int numColumns) {
|
|
int currentByte = 0;
|
|
if (readModule(numRows - 3, 0, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(numRows - 2, 0, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(2, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
currentByte <<= 1;
|
|
if (readModule(3, numColumns - 1, numRows, numColumns)) {
|
|
currentByte |= 1;
|
|
}
|
|
return currentByte;
|
|
}
|
|
|
|
Ref<BitMatrix> BitMatrixParser::extractDataRegion(Ref<BitMatrix> bitMatrix) {
|
|
int symbolSizeRows = parsedVersion_->getSymbolSizeRows();
|
|
int symbolSizeColumns = parsedVersion_->getSymbolSizeColumns();
|
|
|
|
if ((int)bitMatrix->getHeight() != symbolSizeRows) {
|
|
throw IllegalArgumentException("Dimension of bitMatrix must match the version size");
|
|
}
|
|
|
|
int dataRegionSizeRows = parsedVersion_->getDataRegionSizeRows();
|
|
int dataRegionSizeColumns = parsedVersion_->getDataRegionSizeColumns();
|
|
|
|
int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows;
|
|
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
|
|
|
|
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
|
|
int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
|
|
|
|
Ref<BitMatrix> bitMatrixWithoutAlignment(new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow));
|
|
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
|
|
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
|
|
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
|
|
int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns;
|
|
for (int i = 0; i < dataRegionSizeRows; ++i) {
|
|
int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;
|
|
int writeRowOffset = dataRegionRowOffset + i;
|
|
for (int j = 0; j < dataRegionSizeColumns; ++j) {
|
|
int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;
|
|
if (bitMatrix->get(readColumnOffset, readRowOffset)) {
|
|
int writeColumnOffset = dataRegionColumnOffset + j;
|
|
bitMatrixWithoutAlignment->set(writeColumnOffset, writeRowOffset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bitMatrixWithoutAlignment;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/datamatrix/decoder/DataBlock.cpp
|
|
|
|
/*
|
|
* DataBlock.cpp
|
|
* zxing
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/datamatrix/decoder/DataBlock.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
|
|
using namespace std;
|
|
|
|
DataBlock::DataBlock(int numDataCodewords, ArrayRef<unsigned char> codewords) :
|
|
numDataCodewords_(numDataCodewords), codewords_(codewords) {
|
|
}
|
|
|
|
int DataBlock::getNumDataCodewords() {
|
|
return numDataCodewords_;
|
|
}
|
|
|
|
ArrayRef<unsigned char> DataBlock::getCodewords() {
|
|
return codewords_;
|
|
}
|
|
|
|
std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<unsigned char> rawCodewords, Version *version) {
|
|
// Figure out the number and size of data blocks used by this version and
|
|
// error correction level
|
|
ECBlocks* ecBlocks = version->getECBlocks();
|
|
|
|
// First count the total number of data blocks
|
|
int totalBlocks = 0;
|
|
vector<ECB*> ecBlockArray = ecBlocks->getECBlocks();
|
|
for (size_t i = 0; i < ecBlockArray.size(); i++) {
|
|
totalBlocks += ecBlockArray[i]->getCount();
|
|
}
|
|
|
|
// Now establish DataBlocks of the appropriate size and number of data codewords
|
|
std::vector<Ref<DataBlock> > result(totalBlocks);
|
|
int numResultBlocks = 0;
|
|
for (size_t j = 0; j < ecBlockArray.size(); j++) {
|
|
ECB *ecBlock = ecBlockArray[j];
|
|
for (int i = 0; i < ecBlock->getCount(); i++) {
|
|
int numDataCodewords = ecBlock->getDataCodewords();
|
|
int numBlockCodewords = ecBlocks->getECCodewords() + numDataCodewords;
|
|
ArrayRef<unsigned char> buffer(numBlockCodewords);
|
|
Ref<DataBlock> blockRef(new DataBlock(numDataCodewords, buffer));
|
|
result[numResultBlocks++] = blockRef;
|
|
}
|
|
}
|
|
|
|
// All blocks have the same amount of data, except that the last n
|
|
// (where n may be 0) have 1 more byte. Figure out where these start.
|
|
int shorterBlocksTotalCodewords = result[0]->codewords_.size();
|
|
int longerBlocksStartAt = result.size() - 1;
|
|
while (longerBlocksStartAt >= 0) {
|
|
int numCodewords = result[longerBlocksStartAt]->codewords_.size();
|
|
if (numCodewords == shorterBlocksTotalCodewords) {
|
|
break;
|
|
}
|
|
if (numCodewords != shorterBlocksTotalCodewords + 1) {
|
|
throw IllegalArgumentException("Data block sizes differ by more than 1");
|
|
}
|
|
longerBlocksStartAt--;
|
|
}
|
|
longerBlocksStartAt++;
|
|
|
|
int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks->getECCodewords();
|
|
// The last elements of result may be 1 element longer;
|
|
// first fill out as many elements as all of them have
|
|
int rawCodewordsOffset = 0;
|
|
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
|
|
for (int j = 0; j < numResultBlocks; j++) {
|
|
result[j]->codewords_[i] = rawCodewords[rawCodewordsOffset++];
|
|
}
|
|
}
|
|
// Fill out the last data block in the longer ones
|
|
for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
|
|
result[j]->codewords_[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
|
|
}
|
|
// Now add in error correction blocks
|
|
int max = result[0]->codewords_.size();
|
|
for (int i = shorterBlocksNumDataCodewords; i < max; i++) {
|
|
for (int j = 0; j < numResultBlocks; j++) {
|
|
int iOffset = j < longerBlocksStartAt ? i : i + 1;
|
|
result[j]->codewords_[iOffset] = rawCodewords[rawCodewordsOffset++];
|
|
}
|
|
}
|
|
|
|
if ((size_t)rawCodewordsOffset != rawCodewords.size()) {
|
|
throw IllegalArgumentException("rawCodewordsOffset != rawCodewords.length");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/datamatrix/decoder/DecodedBitStreamParser.cpp
|
|
|
|
/*
|
|
* DecodedBitStreamParser.cpp
|
|
* zxing
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/FormatException.h>
|
|
// #include <zxing/datamatrix/decoder/DecodedBitStreamParser.h>
|
|
// #include <iostream>
|
|
// #include <zxing/common/DecoderResult.h>
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
|
|
using namespace std;
|
|
|
|
const char DecodedBitStreamParser::C40_BASIC_SET_CHARS[] = {
|
|
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
|
|
};
|
|
|
|
const char DecodedBitStreamParser::C40_SHIFT2_SET_CHARS[] = {
|
|
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
|
|
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_'
|
|
};
|
|
|
|
const char DecodedBitStreamParser::TEXT_BASIC_SET_CHARS[] = {
|
|
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
|
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
|
|
};
|
|
|
|
const char DecodedBitStreamParser::TEXT_SHIFT3_SET_CHARS[] = {
|
|
'\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127
|
|
};
|
|
|
|
Ref<DecoderResult> DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes) {
|
|
Ref<BitSource> bits(new BitSource(bytes));
|
|
ostringstream result;
|
|
ostringstream resultTrailer;
|
|
vector<unsigned char> byteSegments;
|
|
int mode = ASCII_ENCODE;
|
|
do {
|
|
if (mode == ASCII_ENCODE) {
|
|
mode = decodeAsciiSegment(bits, result, resultTrailer);
|
|
} else {
|
|
switch (mode) {
|
|
case C40_ENCODE:
|
|
decodeC40Segment(bits, result);
|
|
break;
|
|
case TEXT_ENCODE:
|
|
decodeTextSegment(bits, result);
|
|
break;
|
|
case ANSIX12_ENCODE:
|
|
decodeAnsiX12Segment(bits, result);
|
|
break;
|
|
case EDIFACT_ENCODE:
|
|
decodeEdifactSegment(bits, result);
|
|
break;
|
|
case BASE256_ENCODE:
|
|
decodeBase256Segment(bits, result, byteSegments);
|
|
break;
|
|
default:
|
|
throw FormatException("Unsupported mode indicator");
|
|
}
|
|
mode = ASCII_ENCODE;
|
|
}
|
|
} while (mode != PAD_ENCODE && bits->available() > 0);
|
|
|
|
if (resultTrailer.str().size() > 0) {
|
|
result << resultTrailer.str();
|
|
}
|
|
ArrayRef<unsigned char> rawBytes(bytes);
|
|
Ref<String> text(new String(result.str()));
|
|
return Ref<DecoderResult>(new DecoderResult(rawBytes, text));
|
|
}
|
|
|
|
int DecodedBitStreamParser::decodeAsciiSegment(Ref<BitSource> bits, ostringstream & result,
|
|
ostringstream & resultTrailer) {
|
|
bool upperShift = false;
|
|
do {
|
|
int oneByte = bits->readBits(8);
|
|
if (oneByte == 0) {
|
|
throw FormatException("Not enough bits to decode");
|
|
} else if (oneByte <= 128) { // ASCII data (ASCII value + 1)
|
|
oneByte = upperShift ? (oneByte + 128) : oneByte;
|
|
// upperShift = false;
|
|
result << (char) (oneByte - 1);
|
|
return ASCII_ENCODE;
|
|
} else if (oneByte == 129) { // Pad
|
|
return PAD_ENCODE;
|
|
} else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130)
|
|
int value = oneByte - 130;
|
|
if (value < 10) { // padd with '0' for single digit values
|
|
result << '0';
|
|
}
|
|
result << value;
|
|
} else if (oneByte == 230) { // Latch to C40 encodation
|
|
return C40_ENCODE;
|
|
} else if (oneByte == 231) { // Latch to Base 256 encodation
|
|
return BASE256_ENCODE;
|
|
} else if (oneByte == 232) { // FNC1
|
|
result << ((char) 29); // translate as ASCII 29
|
|
} else if (oneByte == 233 || oneByte == 234) {
|
|
// Structured Append, Reader Programming
|
|
// Ignore these symbols for now
|
|
// throw FormatException.getInstance();
|
|
} else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII)
|
|
upperShift = true;
|
|
} else if (oneByte == 236) { // 05 Macro
|
|
result << ("[)>RS05GS");
|
|
resultTrailer << ("RSEOT");
|
|
} else if (oneByte == 237) { // 06 Macro
|
|
result << ("[)>RS06GS");
|
|
resultTrailer << ("RSEOT");
|
|
} else if (oneByte == 238) { // Latch to ANSI X12 encodation
|
|
return ANSIX12_ENCODE;
|
|
} else if (oneByte == 239) { // Latch to Text encodation
|
|
return TEXT_ENCODE;
|
|
} else if (oneByte == 240) { // Latch to EDIFACT encodation
|
|
return EDIFACT_ENCODE;
|
|
} else if (oneByte == 241) { // ECI Character
|
|
// TODO(bbrown): I think we need to support ECI
|
|
// throw FormatException.getInstance();
|
|
// Ignore this symbol for now
|
|
} else if (oneByte >= 242) { // Not to be used in ASCII encodation
|
|
// ... but work around encoders that end with 254, latch back to ASCII
|
|
if (oneByte == 254 && bits->available() == 0) {
|
|
// Ignore
|
|
} else {
|
|
throw FormatException("Not to be used in ASCII encodation");
|
|
}
|
|
}
|
|
} while (bits->available() > 0);
|
|
return ASCII_ENCODE;
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeC40Segment(Ref<BitSource> bits, ostringstream & result) {
|
|
// Three C40 values are encoded in a 16-bit value as
|
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
|
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
|
|
bool upperShift = false;
|
|
|
|
int* cValues = new int[3];
|
|
int shift = 0;
|
|
do {
|
|
// If there is only one byte left then it will be encoded as ASCII
|
|
if (bits->available() == 8) {
|
|
return;
|
|
}
|
|
int firstByte = bits->readBits(8);
|
|
if (firstByte == 254) { // Unlatch codeword
|
|
return;
|
|
}
|
|
|
|
parseTwoBytes(firstByte, bits->readBits(8), cValues);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
int cValue = cValues[i];
|
|
switch (shift) {
|
|
case 0:
|
|
if (cValue < 3) {
|
|
shift = cValue + 1;
|
|
} else {
|
|
if (upperShift) {
|
|
result << (char) (C40_BASIC_SET_CHARS[cValue] + 128);
|
|
upperShift = false;
|
|
} else {
|
|
result << C40_BASIC_SET_CHARS[cValue];
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
if (upperShift) {
|
|
result << (char) (cValue + 128);
|
|
upperShift = false;
|
|
} else {
|
|
result << (char) cValue;
|
|
}
|
|
shift = 0;
|
|
break;
|
|
case 2:
|
|
if (cValue < 27) {
|
|
if (upperShift) {
|
|
result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128);
|
|
upperShift = false;
|
|
} else {
|
|
result << C40_SHIFT2_SET_CHARS[cValue];
|
|
}
|
|
} else if (cValue == 27) { // FNC1
|
|
result << ((char) 29); // translate as ASCII 29
|
|
} else if (cValue == 30) { // Upper Shift
|
|
upperShift = true;
|
|
} else {
|
|
throw FormatException("decodeC40Segment: Upper Shift");
|
|
}
|
|
shift = 0;
|
|
break;
|
|
case 3:
|
|
if (upperShift) {
|
|
result << (char) (cValue + 224);
|
|
upperShift = false;
|
|
} else {
|
|
result << (char) (cValue + 96);
|
|
}
|
|
shift = 0;
|
|
break;
|
|
default:
|
|
throw FormatException("decodeC40Segment: no case");
|
|
}
|
|
}
|
|
} while (bits->available() > 0);
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeTextSegment(Ref<BitSource> bits, ostringstream & result) {
|
|
// Three Text values are encoded in a 16-bit value as
|
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
|
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
|
|
bool upperShift = false;
|
|
|
|
int* cValues = new int[3];
|
|
int shift = 0;
|
|
do {
|
|
// If there is only one byte left then it will be encoded as ASCII
|
|
if (bits->available() == 8) {
|
|
return;
|
|
}
|
|
int firstByte = bits->readBits(8);
|
|
if (firstByte == 254) { // Unlatch codeword
|
|
return;
|
|
}
|
|
|
|
parseTwoBytes(firstByte, bits->readBits(8), cValues);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
int cValue = cValues[i];
|
|
switch (shift) {
|
|
case 0:
|
|
if (cValue < 3) {
|
|
shift = cValue + 1;
|
|
} else {
|
|
if (upperShift) {
|
|
result << (char) (TEXT_BASIC_SET_CHARS[cValue] + 128);
|
|
upperShift = false;
|
|
} else {
|
|
result << (TEXT_BASIC_SET_CHARS[cValue]);
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
if (upperShift) {
|
|
result << (char) (cValue + 128);
|
|
upperShift = false;
|
|
} else {
|
|
result << (char) (cValue);
|
|
}
|
|
shift = 0;
|
|
break;
|
|
case 2:
|
|
// Shift 2 for Text is the same encoding as C40
|
|
if (cValue < 27) {
|
|
if (upperShift) {
|
|
result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128);
|
|
upperShift = false;
|
|
} else {
|
|
result << (C40_SHIFT2_SET_CHARS[cValue]);
|
|
}
|
|
} else if (cValue == 27) { // FNC1
|
|
result << ((char) 29); // translate as ASCII 29
|
|
} else if (cValue == 30) { // Upper Shift
|
|
upperShift = true;
|
|
} else {
|
|
throw FormatException("decodeTextSegment: Upper Shift");
|
|
}
|
|
shift = 0;
|
|
break;
|
|
case 3:
|
|
if (upperShift) {
|
|
result << (char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128);
|
|
upperShift = false;
|
|
} else {
|
|
result << (TEXT_SHIFT3_SET_CHARS[cValue]);
|
|
}
|
|
shift = 0;
|
|
break;
|
|
default:
|
|
throw FormatException("decodeTextSegment: no case");
|
|
}
|
|
}
|
|
} while (bits->available() > 0);
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeAnsiX12Segment(Ref<BitSource> bits, ostringstream & result) {
|
|
// Three ANSI X12 values are encoded in a 16-bit value as
|
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
|
|
|
int* cValues = new int[3];
|
|
do {
|
|
// If there is only one byte left then it will be encoded as ASCII
|
|
if (bits->available() == 8) {
|
|
return;
|
|
}
|
|
int firstByte = bits->readBits(8);
|
|
if (firstByte == 254) { // Unlatch codeword
|
|
return;
|
|
}
|
|
|
|
parseTwoBytes(firstByte, bits->readBits(8), cValues);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
int cValue = cValues[i];
|
|
if (cValue == 0) { // X12 segment terminator <CR>
|
|
result << '\r';
|
|
} else if (cValue == 1) { // X12 segment separator *
|
|
result << '*';
|
|
} else if (cValue == 2) { // X12 sub-element separator >
|
|
result << '>';
|
|
} else if (cValue == 3) { // space
|
|
result << ' ';
|
|
} else if (cValue < 14) { // 0 - 9
|
|
result << (char) (cValue + 44);
|
|
} else if (cValue < 40) { // A - Z
|
|
result << (char) (cValue + 51);
|
|
} else {
|
|
throw FormatException("decodeAnsiX12Segment: no case");
|
|
}
|
|
}
|
|
} while (bits->available() > 0);
|
|
}
|
|
|
|
void DecodedBitStreamParser::parseTwoBytes(int firstByte, int secondByte, int*& result) {
|
|
int fullBitValue = (firstByte << 8) + secondByte - 1;
|
|
int temp = fullBitValue / 1600;
|
|
result[0] = temp;
|
|
fullBitValue -= temp * 1600;
|
|
temp = fullBitValue / 40;
|
|
result[1] = temp;
|
|
result[2] = fullBitValue - temp * 40;
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeEdifactSegment(Ref<BitSource> bits, ostringstream & result) {
|
|
bool unlatch = false;
|
|
do {
|
|
// If there is only two or less bytes left then it will be encoded as ASCII
|
|
if (bits->available() <= 16) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
int edifactValue = bits->readBits(6);
|
|
|
|
// Check for the unlatch character
|
|
if (edifactValue == 0x2B67) { // 011111
|
|
unlatch = true;
|
|
// If we encounter the unlatch code then continue reading because the Codeword triple
|
|
// is padded with 0's
|
|
}
|
|
|
|
if (!unlatch) {
|
|
if ((edifactValue & 0x20) == 0) { // no 1 in the leading (6th) bit
|
|
edifactValue |= 0x40; // Add a leading 01 to the 6 bit binary value
|
|
}
|
|
result << (char)(edifactValue);
|
|
}
|
|
}
|
|
} while (!unlatch && bits->available() > 0);
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeBase256Segment(Ref<BitSource> bits, ostringstream& result, vector<unsigned char> byteSegments) {
|
|
// Figure out how long the Base 256 Segment is.
|
|
int codewordPosition = 1 + bits->getByteOffset(); // position is 1-indexed
|
|
int d1 = unrandomize255State(bits->readBits(8), codewordPosition++);
|
|
int count;
|
|
if (d1 == 0) { // Read the remainder of the symbol
|
|
count = bits->available() / 8;
|
|
} else if (d1 < 250) {
|
|
count = d1;
|
|
} else {
|
|
count = 250 * (d1 - 249) + unrandomize255State(bits->readBits(8), codewordPosition++);
|
|
}
|
|
|
|
// We're seeing NegativeArraySizeException errors from users.
|
|
if (count < 0) {
|
|
throw FormatException("NegativeArraySizeException");
|
|
}
|
|
|
|
unsigned char* bytes = new unsigned char[count];
|
|
for (int i = 0; i < count; i++) {
|
|
// Have seen this particular error in the wild, such as at
|
|
// http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2
|
|
if (bits->available() < 8) {
|
|
throw FormatException("byteSegments");
|
|
}
|
|
bytes[i] = unrandomize255State(bits->readBits(8), codewordPosition++);
|
|
byteSegments.push_back(bytes[i]);
|
|
result << (char)bytes[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// file: zxing/datamatrix/decoder/Decoder.cpp
|
|
|
|
/*
|
|
* Decoder.cpp
|
|
* zxing
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/datamatrix/decoder/Decoder.h>
|
|
// #include <zxing/datamatrix/decoder/BitMatrixParser.h>
|
|
// #include <zxing/datamatrix/decoder/DataBlock.h>
|
|
// #include <zxing/datamatrix/decoder/DecodedBitStreamParser.h>
|
|
// #include <zxing/datamatrix/Version.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <zxing/common/reedsolomon/ReedSolomonException.h>
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
|
|
using namespace std;
|
|
|
|
Decoder::Decoder() :
|
|
rsDecoder_(GF256::DATA_MATRIX_FIELD) {
|
|
}
|
|
|
|
|
|
void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCodewords) {
|
|
int numCodewords = codewordBytes->size();
|
|
ArrayRef<int> codewordInts(numCodewords);
|
|
for (int i = 0; i < numCodewords; i++) {
|
|
codewordInts[i] = codewordBytes[i] & 0xff;
|
|
}
|
|
int numECCodewords = numCodewords - numDataCodewords;
|
|
try {
|
|
rsDecoder_.decode(codewordInts, numECCodewords);
|
|
} catch (ReedSolomonException const& ex) {
|
|
ReaderException rex(ex.what());
|
|
throw rex;
|
|
}
|
|
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
|
// We don't care about errors in the error-correction codewords
|
|
for (int i = 0; i < numDataCodewords; i++) {
|
|
codewordBytes[i] = (unsigned char)codewordInts[i];
|
|
}
|
|
}
|
|
|
|
Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
|
|
// Construct a parser and read version, error-correction level
|
|
BitMatrixParser parser(bits);
|
|
Version *version = parser.readVersion(bits);
|
|
|
|
// Read codewords
|
|
ArrayRef<unsigned char> codewords(parser.readCodewords());
|
|
// Separate into data blocks
|
|
std::vector<Ref<DataBlock> > dataBlocks = DataBlock::getDataBlocks(codewords, version);
|
|
|
|
int dataBlocksCount = dataBlocks.size();
|
|
|
|
// Count total number of data bytes
|
|
int totalBytes = 0;
|
|
for (int i = 0; i < dataBlocksCount; i++) {
|
|
totalBytes += dataBlocks[i]->getNumDataCodewords();
|
|
}
|
|
ArrayRef<unsigned char> resultBytes(totalBytes);
|
|
|
|
// Error-correct and copy data blocks together into a stream of bytes
|
|
for (int j = 0; j < dataBlocksCount; j++) {
|
|
Ref<DataBlock> dataBlock(dataBlocks[j]);
|
|
ArrayRef<unsigned char> codewordBytes = dataBlock->getCodewords();
|
|
int numDataCodewords = dataBlock->getNumDataCodewords();
|
|
correctErrors(codewordBytes, numDataCodewords);
|
|
for (int i = 0; i < numDataCodewords; i++) {
|
|
// De-interlace data blocks.
|
|
resultBytes[i * dataBlocksCount + j] = codewordBytes[i];
|
|
}
|
|
}
|
|
// Decode the contents of that stream of bytes
|
|
DecodedBitStreamParser decodedBSParser;
|
|
return Ref<DecoderResult> (decodedBSParser.decode(resultBytes));
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/datamatrix/detector/CornerPoint.cpp
|
|
|
|
/*
|
|
* CornerPoint.cpp
|
|
* zxing
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/datamatrix/detector/CornerPoint.h>
|
|
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
|
|
using namespace std;
|
|
|
|
CornerPoint::CornerPoint(float posX, float posY) :
|
|
ResultPoint(posX,posY), counter_(0) {
|
|
}
|
|
|
|
int CornerPoint::getCount() const {
|
|
return counter_;
|
|
}
|
|
|
|
void CornerPoint::incrementCount() {
|
|
counter_++;
|
|
}
|
|
|
|
bool CornerPoint::equals(Ref<CornerPoint> other) const {
|
|
return posX_ == other->getX() && posY_ == other->getY();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/datamatrix/detector/Detector.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Detector.cpp
|
|
* zxing
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/ResultPoint.h>
|
|
// #include <zxing/common/GridSampler.h>
|
|
// #include <zxing/datamatrix/detector/Detector.h>
|
|
// #include <cmath>
|
|
// #include <sstream>
|
|
// #include <cstdlib>
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
|
|
using namespace std;
|
|
|
|
ResultPointsAndTransitions::ResultPointsAndTransitions() {
|
|
Ref<ResultPoint> ref(new ResultPoint(0, 0));
|
|
from_ = ref;
|
|
to_ = ref;
|
|
transitions_ = 0;
|
|
}
|
|
|
|
ResultPointsAndTransitions::ResultPointsAndTransitions(Ref<ResultPoint> from, Ref<ResultPoint> to,
|
|
int transitions)
|
|
: to_(to), from_(from), transitions_(transitions) {
|
|
}
|
|
|
|
Ref<ResultPoint> ResultPointsAndTransitions::getFrom() {
|
|
return from_;
|
|
}
|
|
|
|
Ref<ResultPoint> ResultPointsAndTransitions::getTo() {
|
|
return to_;
|
|
}
|
|
|
|
int ResultPointsAndTransitions::getTransitions() {
|
|
return transitions_;
|
|
}
|
|
|
|
Detector::Detector(Ref<BitMatrix> image)
|
|
: image_(image) {
|
|
}
|
|
|
|
Ref<BitMatrix> Detector::getImage() {
|
|
return image_;
|
|
}
|
|
|
|
Ref<DetectorResult> Detector::detect() {
|
|
Ref<WhiteRectangleDetector> rectangleDetector_(new WhiteRectangleDetector(image_));
|
|
std::vector<Ref<ResultPoint> > ResultPoints = rectangleDetector_->detect();
|
|
Ref<ResultPoint> pointA = ResultPoints[0];
|
|
Ref<ResultPoint> pointB = ResultPoints[1];
|
|
Ref<ResultPoint> pointC = ResultPoints[2];
|
|
Ref<ResultPoint> pointD = ResultPoints[3];
|
|
|
|
// Point A and D are across the diagonal from one another,
|
|
// as are B and C. Figure out which are the solid black lines
|
|
// by counting transitions
|
|
std::vector<Ref<ResultPointsAndTransitions> > transitions(4);
|
|
transitions[0].reset(transitionsBetween(pointA, pointB));
|
|
transitions[1].reset(transitionsBetween(pointA, pointC));
|
|
transitions[2].reset(transitionsBetween(pointB, pointD));
|
|
transitions[3].reset(transitionsBetween(pointC, pointD));
|
|
insertionSort(transitions);
|
|
|
|
// Sort by number of transitions. First two will be the two solid sides; last two
|
|
// will be the two alternating black/white sides
|
|
Ref<ResultPointsAndTransitions> lSideOne(transitions[0]);
|
|
Ref<ResultPointsAndTransitions> lSideTwo(transitions[1]);
|
|
|
|
// Figure out which point is their intersection by tallying up the number of times we see the
|
|
// endpoints in the four endpoints. One will show up twice.
|
|
Ref<ResultPoint> maybeTopLeft;
|
|
Ref<ResultPoint> bottomLeft;
|
|
Ref<ResultPoint> maybeBottomRight;
|
|
if (lSideOne->getFrom()->equals(lSideOne->getTo())) {
|
|
bottomLeft = lSideOne->getFrom();
|
|
maybeTopLeft = lSideTwo->getFrom();
|
|
maybeBottomRight = lSideTwo->getTo();
|
|
} else if (lSideOne->getFrom()->equals(lSideTwo->getFrom())) {
|
|
bottomLeft = lSideOne->getFrom();
|
|
maybeTopLeft = lSideOne->getTo();
|
|
maybeBottomRight = lSideTwo->getTo();
|
|
} else if (lSideOne->getFrom()->equals(lSideTwo->getTo())) {
|
|
bottomLeft = lSideOne->getFrom();
|
|
maybeTopLeft = lSideOne->getTo();
|
|
maybeBottomRight = lSideTwo->getFrom();
|
|
} else if (lSideOne->getTo()->equals(lSideTwo->getFrom())) {
|
|
bottomLeft = lSideOne->getTo();
|
|
maybeTopLeft = lSideOne->getFrom();
|
|
maybeBottomRight = lSideTwo->getTo();
|
|
} else if (lSideOne->getTo()->equals(lSideTwo->getTo())) {
|
|
bottomLeft = lSideOne->getTo();
|
|
maybeTopLeft = lSideOne->getFrom();
|
|
maybeBottomRight = lSideTwo->getFrom();
|
|
} else {
|
|
bottomLeft = lSideTwo->getFrom();
|
|
maybeTopLeft = lSideOne->getTo();
|
|
maybeBottomRight = lSideOne->getFrom();
|
|
}
|
|
|
|
// Bottom left is correct but top left and bottom right might be switched
|
|
std::vector<Ref<ResultPoint> > corners(3);
|
|
corners[0].reset(maybeTopLeft);
|
|
corners[1].reset(bottomLeft);
|
|
corners[2].reset(maybeBottomRight);
|
|
|
|
// Use the dot product trick to sort them out
|
|
ResultPoint::orderBestPatterns(corners);
|
|
|
|
// Now we know which is which:
|
|
Ref<ResultPoint> bottomRight(corners[0]);
|
|
bottomLeft = corners[1];
|
|
Ref<ResultPoint> topLeft(corners[2]);
|
|
|
|
// Which point didn't we find in relation to the "L" sides? that's the top right corner
|
|
Ref<ResultPoint> topRight;
|
|
if (!(pointA->equals(bottomRight) || pointA->equals(bottomLeft) || pointA->equals(topLeft))) {
|
|
topRight = pointA;
|
|
} else if (!(pointB->equals(bottomRight) || pointB->equals(bottomLeft)
|
|
|| pointB->equals(topLeft))) {
|
|
topRight = pointB;
|
|
} else if (!(pointC->equals(bottomRight) || pointC->equals(bottomLeft)
|
|
|| pointC->equals(topLeft))) {
|
|
topRight = pointC;
|
|
} else {
|
|
topRight = pointD;
|
|
}
|
|
|
|
// Next determine the dimension by tracing along the top or right side and counting black/white
|
|
// transitions. Since we start inside a black module, we should see a number of transitions
|
|
// equal to 1 less than the code dimension. Well, actually 2 less, because we are going to
|
|
// end on a black module:
|
|
|
|
// The top right point is actually the corner of a module, which is one of the two black modules
|
|
// adjacent to the white module at the top right. Tracing to that corner from either the top left
|
|
// or bottom right should work here.
|
|
|
|
int dimensionTop = transitionsBetween(topLeft, topRight)->getTransitions();
|
|
int dimensionRight = transitionsBetween(bottomRight, topRight)->getTransitions();
|
|
|
|
//dimensionTop++;
|
|
if ((dimensionTop & 0x01) == 1) {
|
|
// it can't be odd, so, round... up?
|
|
dimensionTop++;
|
|
}
|
|
dimensionTop += 2;
|
|
|
|
//dimensionRight++;
|
|
if ((dimensionRight & 0x01) == 1) {
|
|
// it can't be odd, so, round... up?
|
|
dimensionRight++;
|
|
}
|
|
dimensionRight += 2;
|
|
|
|
Ref<BitMatrix> bits;
|
|
Ref<PerspectiveTransform> transform;
|
|
Ref<ResultPoint> correctedTopRight;
|
|
|
|
|
|
// Rectanguar symbols are 6x16, 6x28, 10x24, 10x32, 14x32, or 14x44. If one dimension is more
|
|
// than twice the other, it's certainly rectangular, but to cut a bit more slack we accept it as
|
|
// rectangular if the bigger side is at least 7/4 times the other:
|
|
if (4 * dimensionTop >= 7 * dimensionRight || 4 * dimensionRight >= 7 * dimensionTop) {
|
|
// The matrix is rectangular
|
|
correctedTopRight = correctTopRightRectangular(bottomLeft, bottomRight, topLeft, topRight,
|
|
dimensionTop, dimensionRight);
|
|
if (correctedTopRight == NULL) {
|
|
correctedTopRight = topRight;
|
|
}
|
|
|
|
dimensionTop = transitionsBetween(topLeft, correctedTopRight)->getTransitions();
|
|
dimensionRight = transitionsBetween(bottomRight, correctedTopRight)->getTransitions();
|
|
|
|
if ((dimensionTop & 0x01) == 1) {
|
|
// it can't be odd, so, round... up?
|
|
dimensionTop++;
|
|
}
|
|
|
|
if ((dimensionRight & 0x01) == 1) {
|
|
// it can't be odd, so, round... up?
|
|
dimensionRight++;
|
|
}
|
|
|
|
transform = createTransform(topLeft, correctedTopRight, bottomLeft, bottomRight, dimensionTop,
|
|
dimensionRight);
|
|
bits = sampleGrid(image_, dimensionTop, dimensionRight, transform);
|
|
|
|
} else {
|
|
// The matrix is square
|
|
int dimension = min(dimensionRight, dimensionTop);
|
|
|
|
// correct top right point to match the white module
|
|
correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension);
|
|
if (correctedTopRight == NULL) {
|
|
correctedTopRight = topRight;
|
|
}
|
|
|
|
// Redetermine the dimension using the corrected top right point
|
|
int dimensionCorrected = max(transitionsBetween(topLeft, correctedTopRight)->getTransitions(),
|
|
transitionsBetween(bottomRight, correctedTopRight)->getTransitions());
|
|
dimensionCorrected++;
|
|
if ((dimensionCorrected & 0x01) == 1) {
|
|
dimensionCorrected++;
|
|
}
|
|
|
|
transform = createTransform(topLeft, correctedTopRight, bottomLeft, bottomRight,
|
|
dimensionCorrected, dimensionCorrected);
|
|
bits = sampleGrid(image_, dimensionCorrected, dimensionCorrected, transform);
|
|
}
|
|
|
|
std::vector<Ref<ResultPoint> > points(4);
|
|
points[0].reset(topLeft);
|
|
points[1].reset(bottomLeft);
|
|
points[2].reset(correctedTopRight);
|
|
points[3].reset(bottomRight);
|
|
Ref<DetectorResult> detectorResult(new DetectorResult(bits, points, transform));
|
|
return detectorResult;
|
|
}
|
|
|
|
/**
|
|
* Calculates the position of the white top right module using the output of the rectangle detector
|
|
* for a rectangular matrix
|
|
*/
|
|
Ref<ResultPoint> Detector::correctTopRightRectangular(Ref<ResultPoint> bottomLeft,
|
|
Ref<ResultPoint> bottomRight, Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight,
|
|
int dimensionTop, int dimensionRight) {
|
|
|
|
float corr = distance(bottomLeft, bottomRight) / (float) dimensionTop;
|
|
int norm = distance(topLeft, topRight);
|
|
float cos = (topRight->getX() - topLeft->getX()) / norm;
|
|
float sin = (topRight->getY() - topLeft->getY()) / norm;
|
|
|
|
Ref<ResultPoint> c1(
|
|
new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin));
|
|
|
|
corr = distance(bottomLeft, topLeft) / (float) dimensionRight;
|
|
norm = distance(bottomRight, topRight);
|
|
cos = (topRight->getX() - bottomRight->getX()) / norm;
|
|
sin = (topRight->getY() - bottomRight->getY()) / norm;
|
|
|
|
Ref<ResultPoint> c2(
|
|
new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin));
|
|
|
|
if (!isValid(c1)) {
|
|
if (isValid(c2)) {
|
|
return c2;
|
|
}
|
|
return Ref<ResultPoint>(NULL);
|
|
}
|
|
if (!isValid(c2)) {
|
|
return c1;
|
|
}
|
|
|
|
int l1 = abs(dimensionTop - transitionsBetween(topLeft, c1)->getTransitions())
|
|
+ abs(dimensionRight - transitionsBetween(bottomRight, c1)->getTransitions());
|
|
int l2 = abs(dimensionTop - transitionsBetween(topLeft, c2)->getTransitions())
|
|
+ abs(dimensionRight - transitionsBetween(bottomRight, c2)->getTransitions());
|
|
|
|
return l1 <= l2 ? c1 : c2;
|
|
}
|
|
|
|
/**
|
|
* Calculates the position of the white top right module using the output of the rectangle detector
|
|
* for a square matrix
|
|
*/
|
|
Ref<ResultPoint> Detector::correctTopRight(Ref<ResultPoint> bottomLeft,
|
|
Ref<ResultPoint> bottomRight, Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight,
|
|
int dimension) {
|
|
|
|
float corr = distance(bottomLeft, bottomRight) / (float) dimension;
|
|
int norm = distance(topLeft, topRight);
|
|
float cos = (topRight->getX() - topLeft->getX()) / norm;
|
|
float sin = (topRight->getY() - topLeft->getY()) / norm;
|
|
|
|
Ref<ResultPoint> c1(
|
|
new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin));
|
|
|
|
corr = distance(bottomLeft, bottomRight) / (float) dimension;
|
|
norm = distance(bottomRight, topRight);
|
|
cos = (topRight->getX() - bottomRight->getX()) / norm;
|
|
sin = (topRight->getY() - bottomRight->getY()) / norm;
|
|
|
|
Ref<ResultPoint> c2(
|
|
new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin));
|
|
|
|
if (!isValid(c1)) {
|
|
if (isValid(c2)) {
|
|
return c2;
|
|
}
|
|
return Ref<ResultPoint>(NULL);
|
|
}
|
|
if (!isValid(c2)) {
|
|
return c1;
|
|
}
|
|
|
|
int l1 = abs(
|
|
transitionsBetween(topLeft, c1)->getTransitions()
|
|
- transitionsBetween(bottomRight, c1)->getTransitions());
|
|
int l2 = abs(
|
|
transitionsBetween(topLeft, c2)->getTransitions()
|
|
- transitionsBetween(bottomRight, c2)->getTransitions());
|
|
|
|
return l1 <= l2 ? c1 : c2;
|
|
}
|
|
|
|
bool Detector::isValid(Ref<ResultPoint> p) {
|
|
return p->getX() >= 0 && p->getX() < image_->getWidth() && p->getY() > 0
|
|
&& p->getY() < image_->getHeight();
|
|
}
|
|
|
|
// L2 distance
|
|
int Detector::distance(Ref<ResultPoint> a, Ref<ResultPoint> b) {
|
|
return round(
|
|
(float) sqrt(
|
|
(double) (a->getX() - b->getX()) * (a->getX() - b->getX())
|
|
+ (a->getY() - b->getY()) * (a->getY() - b->getY())));
|
|
}
|
|
|
|
Ref<ResultPointsAndTransitions> Detector::transitionsBetween(Ref<ResultPoint> from,
|
|
Ref<ResultPoint> to) {
|
|
// See QR Code Detector, sizeOfBlackWhiteBlackRun()
|
|
int fromX = (int) from->getX();
|
|
int fromY = (int) from->getY();
|
|
int toX = (int) to->getX();
|
|
int toY = (int) to->getY();
|
|
bool steep = abs(toY - fromY) > abs(toX - fromX);
|
|
if (steep) {
|
|
int temp = fromX;
|
|
fromX = fromY;
|
|
fromY = temp;
|
|
temp = toX;
|
|
toX = toY;
|
|
toY = temp;
|
|
}
|
|
|
|
int dx = abs(toX - fromX);
|
|
int dy = abs(toY - fromY);
|
|
int error = -dx >> 1;
|
|
int ystep = fromY < toY ? 1 : -1;
|
|
int xstep = fromX < toX ? 1 : -1;
|
|
int transitions = 0;
|
|
bool inBlack = image_->get(steep ? fromY : fromX, steep ? fromX : fromY);
|
|
for (int x = fromX, y = fromY; x != toX; x += xstep) {
|
|
bool isBlack = image_->get(steep ? y : x, steep ? x : y);
|
|
if (isBlack != inBlack) {
|
|
transitions++;
|
|
inBlack = isBlack;
|
|
}
|
|
error += dy;
|
|
if (error > 0) {
|
|
if (y == toY) {
|
|
break;
|
|
}
|
|
y += ystep;
|
|
error -= dx;
|
|
}
|
|
}
|
|
Ref<ResultPointsAndTransitions> result(new ResultPointsAndTransitions(from, to, transitions));
|
|
return result;
|
|
}
|
|
|
|
Ref<PerspectiveTransform> Detector::createTransform(Ref<ResultPoint> topLeft,
|
|
Ref<ResultPoint> topRight, Ref<ResultPoint> bottomLeft, Ref<ResultPoint> bottomRight,
|
|
int dimensionX, int dimensionY) {
|
|
|
|
Ref<PerspectiveTransform> transform(
|
|
PerspectiveTransform::quadrilateralToQuadrilateral(
|
|
0.5f,
|
|
0.5f,
|
|
dimensionX - 0.5f,
|
|
0.5f,
|
|
dimensionX - 0.5f,
|
|
dimensionY - 0.5f,
|
|
0.5f,
|
|
dimensionY - 0.5f,
|
|
topLeft->getX(),
|
|
topLeft->getY(),
|
|
topRight->getX(),
|
|
topRight->getY(),
|
|
bottomRight->getX(),
|
|
bottomRight->getY(),
|
|
bottomLeft->getX(),
|
|
bottomLeft->getY()));
|
|
return transform;
|
|
}
|
|
|
|
Ref<BitMatrix> Detector::sampleGrid(Ref<BitMatrix> image, int dimensionX, int dimensionY,
|
|
Ref<PerspectiveTransform> transform) {
|
|
GridSampler &sampler = GridSampler::getInstance();
|
|
return sampler.sampleGrid(image, dimensionX, dimensionY, transform);
|
|
}
|
|
|
|
void Detector::insertionSort(std::vector<Ref<ResultPointsAndTransitions> > &vector) {
|
|
int max = vector.size();
|
|
bool swapped = true;
|
|
Ref<ResultPointsAndTransitions> value;
|
|
Ref<ResultPointsAndTransitions> valueB;
|
|
do {
|
|
swapped = false;
|
|
for (int i = 1; i < max; i++) {
|
|
value = vector[i - 1];
|
|
if (compare(value, (valueB = vector[i])) > 0){
|
|
swapped = true;
|
|
vector[i - 1].reset(valueB);
|
|
vector[i].reset(value);
|
|
}
|
|
}
|
|
} while (swapped);
|
|
}
|
|
|
|
int Detector::compare(Ref<ResultPointsAndTransitions> a, Ref<ResultPointsAndTransitions> b) {
|
|
return a->getTransitions() - b->getTransitions();
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/datamatrix/detector/DetectorException.cpp
|
|
|
|
/*
|
|
* DetectorException.cpp
|
|
*
|
|
* Created on: Aug 26, 2011
|
|
* Author: luiz
|
|
*/
|
|
|
|
// #include "DetectorException.h"
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
|
|
DetectorException::DetectorException(const char *msg) :
|
|
Exception(msg) {
|
|
|
|
}
|
|
|
|
DetectorException::~DetectorException() throw () {
|
|
// TODO Auto-generated destructor stub
|
|
}
|
|
|
|
}
|
|
} /* namespace zxing */
|
|
|
|
// file: zxing/datamatrix/detector/MonochromeRectangleDetector.cpp
|
|
|
|
/*
|
|
* MonochromeRectangleDetector.cpp
|
|
* zxing
|
|
*
|
|
* Created by Luiz Silva on 09/02/2010.
|
|
* Copyright 2010 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/ReaderException.h>
|
|
// #include <zxing/datamatrix/detector/MonochromeRectangleDetector.h>
|
|
// #include <sstream>
|
|
|
|
namespace zxing {
|
|
namespace datamatrix {
|
|
|
|
std::vector<Ref<CornerPoint> > MonochromeRectangleDetector::detect() {
|
|
int height = image_->getHeight();
|
|
int width = image_->getWidth();
|
|
int halfHeight = height >> 1;
|
|
int halfWidth = width >> 1;
|
|
int deltaY = max(1, height / (MAX_MODULES << 3));
|
|
int deltaX = max(1, width / (MAX_MODULES << 3));
|
|
|
|
int top = 0;
|
|
int bottom = height;
|
|
int left = 0;
|
|
int right = width;
|
|
Ref<CornerPoint> pointA(findCornerFromCenter(halfWidth, 0, left, right,
|
|
halfHeight, -deltaY, top, bottom, halfWidth >> 1));
|
|
top = (int) pointA->getY() - 1;
|
|
Ref<CornerPoint> pointB(findCornerFromCenter(halfWidth, -deltaX, left, right,
|
|
halfHeight, 0, top, bottom, halfHeight >> 1));
|
|
left = (int) pointB->getX() - 1;
|
|
Ref<CornerPoint> pointC(findCornerFromCenter(halfWidth, deltaX, left, right,
|
|
halfHeight, 0, top, bottom, halfHeight >> 1));
|
|
right = (int) pointC->getX() + 1;
|
|
Ref<CornerPoint> pointD(findCornerFromCenter(halfWidth, 0, left, right,
|
|
halfHeight, deltaY, top, bottom, halfWidth >> 1));
|
|
bottom = (int) pointD->getY() + 1;
|
|
|
|
// Go try to find point A again with better information -- might have been off at first.
|
|
pointA.reset(findCornerFromCenter(halfWidth, 0, left, right,
|
|
halfHeight, -deltaY, top, bottom, halfWidth >> 2));
|
|
std::vector<Ref<CornerPoint> > corners(4);
|
|
|
|
corners[0].reset(pointA);
|
|
corners[1].reset(pointB);
|
|
corners[2].reset(pointC);
|
|
corners[3].reset(pointD);
|
|
return corners;
|
|
}
|
|
|
|
Ref<CornerPoint> MonochromeRectangleDetector::findCornerFromCenter(int centerX, int deltaX, int left, int right,
|
|
int centerY, int deltaY, int top, int bottom, int maxWhiteRun) {
|
|
Ref<TwoInts> lastRange(NULL);
|
|
for (int y = centerY, x = centerX;
|
|
y < bottom && y >= top && x < right && x >= left;
|
|
y += deltaY, x += deltaX) {
|
|
Ref<TwoInts> range(NULL);
|
|
if (deltaX == 0) {
|
|
// horizontal slices, up and down
|
|
range = blackWhiteRange(y, maxWhiteRun, left, right, true);
|
|
} else {
|
|
// vertical slices, left and right
|
|
range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);
|
|
}
|
|
if (range == NULL) {
|
|
if (lastRange == NULL) {
|
|
throw ReaderException("Couldn't find corners (lastRange = NULL) ");
|
|
} else {
|
|
// lastRange was found
|
|
if (deltaX == 0) {
|
|
int lastY = y - deltaY;
|
|
if (lastRange->start < centerX) {
|
|
if (lastRange->end > centerX) {
|
|
// straddle, choose one or the other based on direction
|
|
Ref<CornerPoint> result(new CornerPoint(deltaY > 0 ? lastRange->start : lastRange->end, lastY));
|
|
return result;
|
|
}
|
|
Ref<CornerPoint> result(new CornerPoint(lastRange->start, lastY));
|
|
return result;
|
|
} else {
|
|
Ref<CornerPoint> result(new CornerPoint(lastRange->end, lastY));
|
|
return result;
|
|
}
|
|
} else {
|
|
int lastX = x - deltaX;
|
|
if (lastRange->start < centerY) {
|
|
if (lastRange->end > centerY) {
|
|
Ref<CornerPoint> result(new CornerPoint(lastX, deltaX < 0 ? lastRange->start : lastRange->end));
|
|
return result;
|
|
}
|
|
Ref<CornerPoint> result(new CornerPoint(lastX, lastRange->start));
|
|
return result;
|
|
} else {
|
|
Ref<CornerPoint> result(new CornerPoint(lastX, lastRange->end));
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
lastRange = range;
|
|
}
|
|
throw ReaderException("Couldn't find corners");
|
|
}
|
|
|
|
Ref<TwoInts> MonochromeRectangleDetector::blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim,
|
|
bool horizontal) {
|
|
|
|
int center = (minDim + maxDim) >> 1;
|
|
|
|
// Scan left/up first
|
|
int start = center;
|
|
while (start >= minDim) {
|
|
if (horizontal ? image_->get(start, fixedDimension) : image_->get(fixedDimension, start)) {
|
|
start--;
|
|
} else {
|
|
int whiteRunStart = start;
|
|
do {
|
|
start--;
|
|
} while (start >= minDim && !(horizontal ? image_->get(start, fixedDimension) :
|
|
image_->get(fixedDimension, start)));
|
|
int whiteRunSize = whiteRunStart - start;
|
|
if (start < minDim || whiteRunSize > maxWhiteRun) {
|
|
start = whiteRunStart;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
start++;
|
|
|
|
// Then try right/down
|
|
int end = center;
|
|
while (end < maxDim) {
|
|
if (horizontal ? image_->get(end, fixedDimension) : image_->get(fixedDimension, end)) {
|
|
end++;
|
|
} else {
|
|
int whiteRunStart = end;
|
|
do {
|
|
end++;
|
|
} while (end < maxDim && !(horizontal ? image_->get(end, fixedDimension) :
|
|
image_->get(fixedDimension, end)));
|
|
int whiteRunSize = end - whiteRunStart;
|
|
if (end >= maxDim || whiteRunSize > maxWhiteRun) {
|
|
end = whiteRunStart;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
end--;
|
|
Ref<TwoInts> result(NULL);
|
|
if (end > start) {
|
|
result = new TwoInts;
|
|
result->start = start;
|
|
result->end = end;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/multi/ByQuadrantReader.cpp
|
|
|
|
/*
|
|
* Copyright 2011 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/multi/ByQuadrantReader.h>
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
namespace multi {
|
|
|
|
ByQuadrantReader::ByQuadrantReader(Reader& delegate) : delegate_(delegate) {}
|
|
|
|
ByQuadrantReader::~ByQuadrantReader(){}
|
|
|
|
Ref<Result> ByQuadrantReader::decode(Ref<BinaryBitmap> image){
|
|
return decode(image, DecodeHints::DEFAULT_HINT);
|
|
}
|
|
|
|
Ref<Result> ByQuadrantReader::decode(Ref<BinaryBitmap> image, DecodeHints hints){
|
|
int width = image->getWidth();
|
|
int height = image->getHeight();
|
|
int halfWidth = width / 2;
|
|
int halfHeight = height / 2;
|
|
Ref<BinaryBitmap> topLeft = image->crop(0, 0, halfWidth, halfHeight);
|
|
try {
|
|
return delegate_.decode(topLeft, hints);
|
|
} catch (ReaderException re) {
|
|
// continue
|
|
}
|
|
|
|
Ref<BinaryBitmap> topRight = image->crop(halfWidth, 0, halfWidth, halfHeight);
|
|
try {
|
|
return delegate_.decode(topRight, hints);
|
|
} catch (ReaderException re) {
|
|
// continue
|
|
}
|
|
|
|
Ref<BinaryBitmap> bottomLeft = image->crop(0, halfHeight, halfWidth, halfHeight);
|
|
try {
|
|
return delegate_.decode(bottomLeft, hints);
|
|
} catch (ReaderException re) {
|
|
// continue
|
|
}
|
|
|
|
Ref<BinaryBitmap> bottomRight = image->crop(halfWidth, halfHeight, halfWidth, halfHeight);
|
|
try {
|
|
return delegate_.decode(bottomRight, hints);
|
|
} catch (ReaderException re) {
|
|
// continue
|
|
}
|
|
|
|
int quarterWidth = halfWidth / 2;
|
|
int quarterHeight = halfHeight / 2;
|
|
Ref<BinaryBitmap> center = image->crop(quarterWidth, quarterHeight, halfWidth, halfHeight);
|
|
return delegate_.decode(center, hints);
|
|
}
|
|
|
|
} // End zxing::multi namespace
|
|
} // End zxing namespace
|
|
|
|
// file: zxing/multi/GenericMultipleBarcodeReader.cpp
|
|
|
|
/*
|
|
* Copyright 2011 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/multi/GenericMultipleBarcodeReader.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <zxing/ResultPoint.h>
|
|
|
|
namespace zxing {
|
|
namespace multi {
|
|
GenericMultipleBarcodeReader::GenericMultipleBarcodeReader(Reader& delegate) :
|
|
delegate_(delegate)
|
|
{
|
|
}
|
|
|
|
GenericMultipleBarcodeReader::~GenericMultipleBarcodeReader(){}
|
|
|
|
std::vector<Ref<Result> > GenericMultipleBarcodeReader::decodeMultiple(
|
|
Ref<BinaryBitmap> image, DecodeHints hints)
|
|
{
|
|
std::vector<Ref<Result> > results;
|
|
doDecodeMultiple(image, hints, results, 0, 0);
|
|
if (results.empty()){
|
|
throw ReaderException("No code detected");
|
|
}
|
|
return results;
|
|
}
|
|
|
|
void GenericMultipleBarcodeReader::doDecodeMultiple(Ref<BinaryBitmap> image,
|
|
DecodeHints hints, std::vector<Ref<Result> >& results, int xOffset, int yOffset)
|
|
{
|
|
Ref<Result> result;
|
|
try {
|
|
result = delegate_.decode(image, hints);
|
|
} catch (ReaderException re) {
|
|
return;
|
|
}
|
|
bool alreadyFound = false;
|
|
for (unsigned int i = 0; i < results.size(); i++) {
|
|
Ref<Result> existingResult = results[i];
|
|
if (existingResult->getText()->getText() == result->getText()->getText()) {
|
|
alreadyFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (alreadyFound) {
|
|
return;
|
|
}
|
|
|
|
results.push_back(translateResultPoints(result, xOffset, yOffset));
|
|
const std::vector<Ref<ResultPoint> > resultPoints = result->getResultPoints();
|
|
if (resultPoints.empty()) {
|
|
return;
|
|
}
|
|
|
|
int width = image->getWidth();
|
|
int height = image->getHeight();
|
|
float minX = width;
|
|
float minY = height;
|
|
float maxX = 0.0f;
|
|
float maxY = 0.0f;
|
|
for (unsigned int i = 0; i < resultPoints.size(); i++) {
|
|
Ref<ResultPoint> point = resultPoints[i];
|
|
float x = point->getX();
|
|
float y = point->getY();
|
|
if (x < minX) {
|
|
minX = x;
|
|
}
|
|
if (y < minY) {
|
|
minY = y;
|
|
}
|
|
if (x > maxX) {
|
|
maxX = x;
|
|
}
|
|
if (y > maxY) {
|
|
maxY = y;
|
|
}
|
|
}
|
|
|
|
// Decode left of barcode
|
|
if (minX > MIN_DIMENSION_TO_RECUR) {
|
|
doDecodeMultiple(image->crop(0, 0, (int) minX, height),
|
|
hints, results, xOffset, yOffset);
|
|
}
|
|
// Decode above barcode
|
|
if (minY > MIN_DIMENSION_TO_RECUR) {
|
|
doDecodeMultiple(image->crop(0, 0, width, (int) minY),
|
|
hints, results, xOffset, yOffset);
|
|
}
|
|
// Decode right of barcode
|
|
if (maxX < width - MIN_DIMENSION_TO_RECUR) {
|
|
doDecodeMultiple(image->crop((int) maxX, 0, width - (int) maxX, height),
|
|
hints, results, xOffset + (int) maxX, yOffset);
|
|
}
|
|
// Decode below barcode
|
|
if (maxY < height - MIN_DIMENSION_TO_RECUR) {
|
|
doDecodeMultiple(image->crop(0, (int) maxY, width, height - (int) maxY),
|
|
hints, results, xOffset, yOffset + (int) maxY);
|
|
}
|
|
}
|
|
|
|
Ref<Result> GenericMultipleBarcodeReader::translateResultPoints(Ref<Result> result, int xOffset, int yOffset){
|
|
const std::vector<Ref<ResultPoint> > oldResultPoints = result->getResultPoints();
|
|
if (oldResultPoints.empty()) {
|
|
return result;
|
|
}
|
|
std::vector<Ref<ResultPoint> > newResultPoints;
|
|
for (unsigned int i = 0; i < oldResultPoints.size(); i++) {
|
|
Ref<ResultPoint> oldPoint = oldResultPoints[i];
|
|
newResultPoints.push_back(Ref<ResultPoint>(new ResultPoint(oldPoint->getX() + xOffset, oldPoint->getY() + yOffset)));
|
|
}
|
|
return Ref<Result>(new Result(result->getText(), result->getRawBytes(), newResultPoints, result->getBarcodeFormat()));
|
|
}
|
|
|
|
} // End zxing::multi namespace
|
|
} // End zxing namespace
|
|
|
|
// file: zxing/multi/MultipleBarcodeReader.cpp
|
|
|
|
/*
|
|
* Copyright 2011 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/multi/MultipleBarcodeReader.h>
|
|
|
|
namespace zxing {
|
|
namespace multi {
|
|
|
|
MultipleBarcodeReader::~MultipleBarcodeReader() { }
|
|
|
|
std::vector<Ref<Result> > MultipleBarcodeReader::decodeMultiple(Ref<BinaryBitmap> image) {
|
|
return decodeMultiple(image, DecodeHints::DEFAULT_HINT);
|
|
}
|
|
|
|
} // End zxing::multi namespace
|
|
} // End zxing namespace
|
|
|
|
// file: zxing/multi/qrcode/QRCodeMultiReader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2011 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/multi/qrcode/QRCodeMultiReader.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <zxing/multi/qrcode/detector/MultiDetector.h>
|
|
// #include <zxing/BarcodeFormat.h>
|
|
|
|
namespace zxing {
|
|
namespace multi {
|
|
QRCodeMultiReader::QRCodeMultiReader(){}
|
|
|
|
QRCodeMultiReader::~QRCodeMultiReader(){}
|
|
|
|
std::vector<Ref<Result> > QRCodeMultiReader::decodeMultiple(Ref<BinaryBitmap> image,
|
|
DecodeHints hints)
|
|
{
|
|
std::vector<Ref<Result> > results;
|
|
MultiDetector detector(image->getBlackMatrix());
|
|
|
|
std::vector<Ref<DetectorResult> > detectorResult = detector.detectMulti(hints);
|
|
for (unsigned int i = 0; i < detectorResult.size(); i++) {
|
|
try {
|
|
Ref<DecoderResult> decoderResult = getDecoder().decode(detectorResult[i]->getBits());
|
|
std::vector<Ref<ResultPoint> > points = detectorResult[i]->getPoints();
|
|
Ref<Result> result = Ref<Result>(new Result(decoderResult->getText(),
|
|
decoderResult->getRawBytes(),
|
|
points, BarcodeFormat_QR_CODE));
|
|
// result->putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult->getByteSegments());
|
|
// result->putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult->getECLevel().toString());
|
|
results.push_back(result);
|
|
} catch (ReaderException re) {
|
|
// ignore and continue
|
|
}
|
|
}
|
|
if (results.empty()){
|
|
throw ReaderException("No code detected");
|
|
}
|
|
return results;
|
|
}
|
|
|
|
} // End zxing::multi namespace
|
|
} // End zxing namespace
|
|
|
|
// file: zxing/multi/qrcode/detector/MultiDetector.cpp
|
|
|
|
/*
|
|
* Copyright 2011 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.
|
|
*/
|
|
|
|
// #include <zxing/multi/qrcode/detector/MultiDetector.h>
|
|
// #include <zxing/multi/qrcode/detector/MultiFinderPatternFinder.h>
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
namespace multi {
|
|
using namespace zxing::qrcode;
|
|
|
|
MultiDetector::MultiDetector(Ref<BitMatrix> image) : Detector(image) {}
|
|
|
|
MultiDetector::~MultiDetector(){}
|
|
|
|
std::vector<Ref<DetectorResult> > MultiDetector::detectMulti(DecodeHints hints){
|
|
Ref<BitMatrix> image = getImage();
|
|
MultiFinderPatternFinder finder = MultiFinderPatternFinder(image, hints.getResultPointCallback());
|
|
std::vector<Ref<FinderPatternInfo> > info = finder.findMulti(hints);
|
|
std::vector<Ref<DetectorResult> > result;
|
|
for(unsigned int i = 0; i < info.size(); i++){
|
|
try{
|
|
result.push_back(processFinderPatternInfo(info[i]));
|
|
} catch (ReaderException e){
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
} // End zxing::multi namespace
|
|
} // End zxing namespace
|
|
|
|
// file: zxing/multi/qrcode/detector/MultiFinderPatternFinder.cpp
|
|
|
|
/*
|
|
* Copyright 2011 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.
|
|
*/
|
|
|
|
// #include <algorithm>
|
|
// #include <math.h>
|
|
// #include <stdlib.h>
|
|
// #include <zxing/multi/qrcode/detector/MultiFinderPatternFinder.h>
|
|
// #include <zxing/DecodeHints.h>
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing{
|
|
namespace multi {
|
|
using namespace zxing::qrcode;
|
|
|
|
const float MultiFinderPatternFinder::MAX_MODULE_COUNT_PER_EDGE = 180;
|
|
const float MultiFinderPatternFinder::MIN_MODULE_COUNT_PER_EDGE = 9;
|
|
const float MultiFinderPatternFinder::DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f;
|
|
const float MultiFinderPatternFinder::DIFF_MODSIZE_CUTOFF = 0.5f;
|
|
|
|
bool compareModuleSize(Ref<FinderPattern> a, Ref<FinderPattern> b){
|
|
float value = a->getEstimatedModuleSize() - b->getEstimatedModuleSize();
|
|
return value < 0.0;
|
|
}
|
|
|
|
|
|
MultiFinderPatternFinder::MultiFinderPatternFinder(Ref<BitMatrix> image,
|
|
Ref<ResultPointCallback> resultPointCallback) :
|
|
FinderPatternFinder(image, resultPointCallback)
|
|
{
|
|
}
|
|
|
|
MultiFinderPatternFinder::~MultiFinderPatternFinder(){}
|
|
|
|
std::vector<Ref<FinderPatternInfo> > MultiFinderPatternFinder::findMulti(DecodeHints const& hints){
|
|
bool tryHarder = hints.getTryHarder();
|
|
Ref<BitMatrix> image = image_; // Protected member
|
|
int maxI = image->getHeight();
|
|
int maxJ = image->getWidth();
|
|
// We are looking for black/white/black/white/black modules in
|
|
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
|
|
|
|
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
|
|
// image, and then account for the center being 3 modules in size. This gives the smallest
|
|
// number of pixels the center could be, so skip this often. When trying harder, look for all
|
|
// QR versions regardless of how dense they are.
|
|
int iSkip = (int) (maxI / (MAX_MODULES * 4.0f) * 3);
|
|
if (iSkip < MIN_SKIP || tryHarder) {
|
|
iSkip = MIN_SKIP;
|
|
}
|
|
|
|
int stateCount[5];
|
|
for (int i = iSkip - 1; i < maxI; i += iSkip) {
|
|
// Get a row of black/white values
|
|
stateCount[0] = 0;
|
|
stateCount[1] = 0;
|
|
stateCount[2] = 0;
|
|
stateCount[3] = 0;
|
|
stateCount[4] = 0;
|
|
int currentState = 0;
|
|
for (int j = 0; j < maxJ; j++) {
|
|
if (image->get(j, i)) {
|
|
// Black pixel
|
|
if ((currentState & 1) == 1) { // Counting white pixels
|
|
currentState++;
|
|
}
|
|
stateCount[currentState]++;
|
|
} else { // White pixel
|
|
if ((currentState & 1) == 0) { // Counting black pixels
|
|
if (currentState == 4) { // A winner?
|
|
if (foundPatternCross(stateCount)) { // Yes
|
|
bool confirmed = handlePossibleCenter(stateCount, i, j);
|
|
if (!confirmed) {
|
|
do { // Advance to next black pixel
|
|
j++;
|
|
} while (j < maxJ && !image->get(j, i));
|
|
j--; // back up to that last white pixel
|
|
}
|
|
// Clear state to start looking again
|
|
currentState = 0;
|
|
stateCount[0] = 0;
|
|
stateCount[1] = 0;
|
|
stateCount[2] = 0;
|
|
stateCount[3] = 0;
|
|
stateCount[4] = 0;
|
|
} else { // No, shift counts back by two
|
|
stateCount[0] = stateCount[2];
|
|
stateCount[1] = stateCount[3];
|
|
stateCount[2] = stateCount[4];
|
|
stateCount[3] = 1;
|
|
stateCount[4] = 0;
|
|
currentState = 3;
|
|
}
|
|
} else {
|
|
stateCount[++currentState]++;
|
|
}
|
|
} else { // Counting white pixels
|
|
stateCount[currentState]++;
|
|
}
|
|
}
|
|
} // for j=...
|
|
|
|
if (foundPatternCross(stateCount)) {
|
|
handlePossibleCenter(stateCount, i, maxJ);
|
|
} // end if foundPatternCross
|
|
} // for i=iSkip-1 ...
|
|
std::vector<std::vector<Ref<FinderPattern> > > patternInfo = selectBestPatterns();
|
|
std::vector<Ref<FinderPatternInfo> > result;
|
|
for (unsigned int i = 0; i < patternInfo.size(); i++) {
|
|
std::vector<Ref<FinderPattern> > pattern = patternInfo[i];
|
|
FinderPatternFinder::orderBestPatterns(pattern);
|
|
result.push_back(Ref<FinderPatternInfo>(new FinderPatternInfo(pattern)));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<std::vector<Ref<FinderPattern> > > MultiFinderPatternFinder::selectBestPatterns(){
|
|
std::vector<Ref<FinderPattern> > possibleCenters = possibleCenters_;
|
|
|
|
int size = possibleCenters.size();
|
|
|
|
if (size < 3) {
|
|
// Couldn't find enough finder patterns
|
|
throw ReaderException("No code detected");
|
|
}
|
|
|
|
std::vector<std::vector<Ref<FinderPattern> > > results;
|
|
|
|
/*
|
|
* Begin HE modifications to safely detect multiple codes of equal size
|
|
*/
|
|
if (size == 3) {
|
|
results.push_back(possibleCenters_);
|
|
return results;
|
|
}
|
|
|
|
// Sort by estimated module size to speed up the upcoming checks
|
|
//TODO do a sort based on module size
|
|
std::sort(possibleCenters.begin(), possibleCenters.end(), compareModuleSize);
|
|
|
|
/*
|
|
* Now lets start: build a list of tuples of three finder locations that
|
|
* - feature similar module sizes
|
|
* - are placed in a distance so the estimated module count is within the QR specification
|
|
* - have similar distance between upper left/right and left top/bottom finder patterns
|
|
* - form a triangle with 90° angle (checked by comparing top right/bottom left distance
|
|
* with pythagoras)
|
|
*
|
|
* Note: we allow each point to be used for more than one code region: this might seem
|
|
* counterintuitive at first, but the performance penalty is not that big. At this point,
|
|
* we cannot make a good quality decision whether the three finders actually represent
|
|
* a QR code, or are just by chance layouted so it looks like there might be a QR code there.
|
|
* So, if the layout seems right, lets have the decoder try to decode.
|
|
*/
|
|
|
|
for (int i1 = 0; i1 < (size - 2); i1++) {
|
|
Ref<FinderPattern> p1 = possibleCenters[i1];
|
|
for (int i2 = i1 + 1; i2 < (size - 1); i2++) {
|
|
Ref<FinderPattern> p2 = possibleCenters[i2];
|
|
// Compare the expected module sizes; if they are really off, skip
|
|
float vModSize12 = (p1->getEstimatedModuleSize() - p2->getEstimatedModuleSize()) / std::min(p1->getEstimatedModuleSize(), p2->getEstimatedModuleSize());
|
|
float vModSize12A = abs(p1->getEstimatedModuleSize() - p2->getEstimatedModuleSize());
|
|
if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
|
|
// break, since elements are ordered by the module size deviation there cannot be
|
|
// any more interesting elements for the given p1.
|
|
break;
|
|
}
|
|
for (int i3 = i2 + 1; i3 < size; i3++) {
|
|
Ref<FinderPattern> p3 = possibleCenters[i3];
|
|
// Compare the expected module sizes; if they are really off, skip
|
|
float vModSize23 = (p2->getEstimatedModuleSize() - p3->getEstimatedModuleSize()) / std::min(p2->getEstimatedModuleSize(), p3->getEstimatedModuleSize());
|
|
float vModSize23A = abs(p2->getEstimatedModuleSize() - p3->getEstimatedModuleSize());
|
|
if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
|
|
// break, since elements are ordered by the module size deviation there cannot be
|
|
// any more interesting elements for the given p1.
|
|
break;
|
|
}
|
|
std::vector<Ref<FinderPattern> > test;
|
|
test.push_back(p1);
|
|
test.push_back(p2);
|
|
test.push_back(p3);
|
|
FinderPatternFinder::orderBestPatterns(test);
|
|
// Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
|
|
Ref<FinderPatternInfo> info = Ref<FinderPatternInfo>(new FinderPatternInfo(test));
|
|
float dA = FinderPatternFinder::distance(info->getTopLeft(), info->getBottomLeft());
|
|
float dC = FinderPatternFinder::distance(info->getTopRight(), info->getBottomLeft());
|
|
float dB = FinderPatternFinder::distance(info->getTopLeft(), info->getTopRight());
|
|
// Check the sizes
|
|
float estimatedModuleCount = (dA + dB) / (p1->getEstimatedModuleSize() * 2.0f);
|
|
if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) {
|
|
continue;
|
|
}
|
|
// Calculate the difference of the edge lengths in percent
|
|
float vABBC = abs((dA - dB) / std::min(dA, dB));
|
|
if (vABBC >= 0.1f) {
|
|
continue;
|
|
}
|
|
// Calculate the diagonal length by assuming a 90° angle at topleft
|
|
float dCpy = (float) sqrt(dA * dA + dB * dB);
|
|
// Compare to the real distance in %
|
|
float vPyC = abs((dC - dCpy) / std::min(dC, dCpy));
|
|
if (vPyC >= 0.1f) {
|
|
continue;
|
|
}
|
|
// All tests passed!
|
|
results.push_back(test);
|
|
} // end iterate p3
|
|
} // end iterate p2
|
|
} // end iterate p1
|
|
if (results.empty()){
|
|
// Nothing found!
|
|
throw ReaderException("No code detected");
|
|
}
|
|
return results;
|
|
}
|
|
|
|
} // End zxing::multi namespace
|
|
} // End zxing namespace
|
|
|
|
// file: zxing/oned/Code128Reader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2010 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 "Code128Reader.h"
|
|
// #include <zxing/oned/OneDResultPoint.h>
|
|
// #include <zxing/common/Array.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <math.h>
|
|
// #include <string.h>
|
|
// #include <sstream>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
|
|
const int CODE_PATTERNS_LENGTH = 107;
|
|
const int countersLength = 6;
|
|
static const int CODE_PATTERNS[CODE_PATTERNS_LENGTH][countersLength] = {
|
|
{2, 1, 2, 2, 2, 2}, /* 0 */
|
|
{2, 2, 2, 1, 2, 2},
|
|
{2, 2, 2, 2, 2, 1},
|
|
{1, 2, 1, 2, 2, 3},
|
|
{1, 2, 1, 3, 2, 2},
|
|
{1, 3, 1, 2, 2, 2}, /* 5 */
|
|
{1, 2, 2, 2, 1, 3},
|
|
{1, 2, 2, 3, 1, 2},
|
|
{1, 3, 2, 2, 1, 2},
|
|
{2, 2, 1, 2, 1, 3},
|
|
{2, 2, 1, 3, 1, 2}, /* 10 */
|
|
{2, 3, 1, 2, 1, 2},
|
|
{1, 1, 2, 2, 3, 2},
|
|
{1, 2, 2, 1, 3, 2},
|
|
{1, 2, 2, 2, 3, 1},
|
|
{1, 1, 3, 2, 2, 2}, /* 15 */
|
|
{1, 2, 3, 1, 2, 2},
|
|
{1, 2, 3, 2, 2, 1},
|
|
{2, 2, 3, 2, 1, 1},
|
|
{2, 2, 1, 1, 3, 2},
|
|
{2, 2, 1, 2, 3, 1}, /* 20 */
|
|
{2, 1, 3, 2, 1, 2},
|
|
{2, 2, 3, 1, 1, 2},
|
|
{3, 1, 2, 1, 3, 1},
|
|
{3, 1, 1, 2, 2, 2},
|
|
{3, 2, 1, 1, 2, 2}, /* 25 */
|
|
{3, 2, 1, 2, 2, 1},
|
|
{3, 1, 2, 2, 1, 2},
|
|
{3, 2, 2, 1, 1, 2},
|
|
{3, 2, 2, 2, 1, 1},
|
|
{2, 1, 2, 1, 2, 3}, /* 30 */
|
|
{2, 1, 2, 3, 2, 1},
|
|
{2, 3, 2, 1, 2, 1},
|
|
{1, 1, 1, 3, 2, 3},
|
|
{1, 3, 1, 1, 2, 3},
|
|
{1, 3, 1, 3, 2, 1}, /* 35 */
|
|
{1, 1, 2, 3, 1, 3},
|
|
{1, 3, 2, 1, 1, 3},
|
|
{1, 3, 2, 3, 1, 1},
|
|
{2, 1, 1, 3, 1, 3},
|
|
{2, 3, 1, 1, 1, 3}, /* 40 */
|
|
{2, 3, 1, 3, 1, 1},
|
|
{1, 1, 2, 1, 3, 3},
|
|
{1, 1, 2, 3, 3, 1},
|
|
{1, 3, 2, 1, 3, 1},
|
|
{1, 1, 3, 1, 2, 3}, /* 45 */
|
|
{1, 1, 3, 3, 2, 1},
|
|
{1, 3, 3, 1, 2, 1},
|
|
{3, 1, 3, 1, 2, 1},
|
|
{2, 1, 1, 3, 3, 1},
|
|
{2, 3, 1, 1, 3, 1}, /* 50 */
|
|
{2, 1, 3, 1, 1, 3},
|
|
{2, 1, 3, 3, 1, 1},
|
|
{2, 1, 3, 1, 3, 1},
|
|
{3, 1, 1, 1, 2, 3},
|
|
{3, 1, 1, 3, 2, 1}, /* 55 */
|
|
{3, 3, 1, 1, 2, 1},
|
|
{3, 1, 2, 1, 1, 3},
|
|
{3, 1, 2, 3, 1, 1},
|
|
{3, 3, 2, 1, 1, 1},
|
|
{3, 1, 4, 1, 1, 1}, /* 60 */
|
|
{2, 2, 1, 4, 1, 1},
|
|
{4, 3, 1, 1, 1, 1},
|
|
{1, 1, 1, 2, 2, 4},
|
|
{1, 1, 1, 4, 2, 2},
|
|
{1, 2, 1, 1, 2, 4}, /* 65 */
|
|
{1, 2, 1, 4, 2, 1},
|
|
{1, 4, 1, 1, 2, 2},
|
|
{1, 4, 1, 2, 2, 1},
|
|
{1, 1, 2, 2, 1, 4},
|
|
{1, 1, 2, 4, 1, 2}, /* 70 */
|
|
{1, 2, 2, 1, 1, 4},
|
|
{1, 2, 2, 4, 1, 1},
|
|
{1, 4, 2, 1, 1, 2},
|
|
{1, 4, 2, 2, 1, 1},
|
|
{2, 4, 1, 2, 1, 1}, /* 75 */
|
|
{2, 2, 1, 1, 1, 4},
|
|
{4, 1, 3, 1, 1, 1},
|
|
{2, 4, 1, 1, 1, 2},
|
|
{1, 3, 4, 1, 1, 1},
|
|
{1, 1, 1, 2, 4, 2}, /* 80 */
|
|
{1, 2, 1, 1, 4, 2},
|
|
{1, 2, 1, 2, 4, 1},
|
|
{1, 1, 4, 2, 1, 2},
|
|
{1, 2, 4, 1, 1, 2},
|
|
{1, 2, 4, 2, 1, 1}, /* 85 */
|
|
{4, 1, 1, 2, 1, 2},
|
|
{4, 2, 1, 1, 1, 2},
|
|
{4, 2, 1, 2, 1, 1},
|
|
{2, 1, 2, 1, 4, 1},
|
|
{2, 1, 4, 1, 2, 1}, /* 90 */
|
|
{4, 1, 2, 1, 2, 1},
|
|
{1, 1, 1, 1, 4, 3},
|
|
{1, 1, 1, 3, 4, 1},
|
|
{1, 3, 1, 1, 4, 1},
|
|
{1, 1, 4, 1, 1, 3}, /* 95 */
|
|
{1, 1, 4, 3, 1, 1},
|
|
{4, 1, 1, 1, 1, 3},
|
|
{4, 1, 1, 3, 1, 1},
|
|
{1, 1, 3, 1, 4, 1},
|
|
{1, 1, 4, 1, 3, 1}, /* 100 */
|
|
{3, 1, 1, 1, 4, 1},
|
|
{4, 1, 1, 1, 3, 1},
|
|
{2, 1, 1, 4, 1, 2},
|
|
{2, 1, 1, 2, 1, 4},
|
|
{2, 1, 1, 2, 3, 2}, /* 105 */
|
|
{2, 3, 3, 1, 1, 1}
|
|
};
|
|
|
|
|
|
Code128Reader::Code128Reader(){
|
|
}
|
|
|
|
int* Code128Reader::findStartPattern(Ref<BitArray> row){
|
|
int width = row->getSize();
|
|
int rowOffset = 0;
|
|
while (rowOffset < width) {
|
|
if (row->get(rowOffset)) {
|
|
break;
|
|
}
|
|
rowOffset++;
|
|
}
|
|
|
|
int counterPosition = 0;
|
|
int counters[countersLength] = {0,0,0,0,0,0};
|
|
int patternStart = rowOffset;
|
|
bool isWhite = false;
|
|
int patternLength = sizeof(counters) / sizeof(int);
|
|
|
|
for (int i = rowOffset; i < width; i++) {
|
|
bool pixel = row->get(i);
|
|
if (pixel ^ isWhite) {
|
|
counters[counterPosition]++;
|
|
} else {
|
|
if (counterPosition == patternLength - 1) {
|
|
unsigned int bestVariance = MAX_AVG_VARIANCE;
|
|
int bestMatch = -1;
|
|
for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
|
|
unsigned int variance = patternMatchVariance(counters, sizeof(counters) / sizeof(int),
|
|
CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE);
|
|
if (variance < bestVariance) {
|
|
bestVariance = variance;
|
|
bestMatch = startCode;
|
|
}
|
|
}
|
|
if (bestMatch >= 0) {
|
|
// Look for whitespace before start pattern, >= 50% of width of start pattern
|
|
if (row->isRange(std::max(0, patternStart - (i - patternStart) / 2), patternStart,
|
|
false)) {
|
|
int* resultValue = new int[3];
|
|
resultValue[0] = patternStart;
|
|
resultValue[1] = i;
|
|
resultValue[2] = bestMatch;
|
|
return resultValue;
|
|
}
|
|
}
|
|
patternStart += counters[0] + counters[1];
|
|
for (int y = 2; y < patternLength; y++) {
|
|
counters[y - 2] = counters[y];
|
|
}
|
|
counters[patternLength - 2] = 0;
|
|
counters[patternLength - 1] = 0;
|
|
counterPosition--;
|
|
} else {
|
|
counterPosition++;
|
|
}
|
|
counters[counterPosition] = 1;
|
|
isWhite = !isWhite;
|
|
}
|
|
}
|
|
throw ReaderException("");
|
|
}
|
|
|
|
int Code128Reader::decodeCode(Ref<BitArray> row, int counters[], int countersCount,
|
|
int rowOffset) {
|
|
if (!recordPattern(row, rowOffset, counters, countersCount)) {
|
|
throw ReaderException("");
|
|
}
|
|
unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
|
int bestMatch = -1;
|
|
for (int d = 0; d < CODE_PATTERNS_LENGTH; d++) {
|
|
int pattern[countersLength];
|
|
|
|
for(int ind = 0; ind< countersLength; ind++){
|
|
pattern[ind] = CODE_PATTERNS[d][ind];
|
|
}
|
|
// memcpy(pattern, CODE_PATTERNS[d], countersLength);
|
|
unsigned int variance = patternMatchVariance(counters, countersCount, pattern,
|
|
MAX_INDIVIDUAL_VARIANCE);
|
|
if (variance < bestVariance) {
|
|
bestVariance = variance;
|
|
bestMatch = d;
|
|
}
|
|
}
|
|
// TODO We're overlooking the fact that the STOP pattern has 7 values, not 6.
|
|
if (bestMatch >= 0) {
|
|
return bestMatch;
|
|
} else {
|
|
throw ReaderException("");
|
|
}
|
|
}
|
|
|
|
Ref<Result> Code128Reader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
|
int* startPatternInfo = NULL;
|
|
try {
|
|
startPatternInfo = findStartPattern(row);
|
|
int startCode = startPatternInfo[2];
|
|
int codeSet;
|
|
switch (startCode) {
|
|
case CODE_START_A:
|
|
codeSet = CODE_CODE_A;
|
|
break;
|
|
case CODE_START_B:
|
|
codeSet = CODE_CODE_B;
|
|
break;
|
|
case CODE_START_C:
|
|
codeSet = CODE_CODE_C;
|
|
break;
|
|
default:
|
|
throw ReaderException("");
|
|
}
|
|
|
|
bool done = false;
|
|
bool isNextShifted = false;
|
|
|
|
std::string tmpResultString;
|
|
std::stringstream tmpResultSStr; // used if its Code 128C
|
|
|
|
int lastStart = startPatternInfo[0];
|
|
int nextStart = startPatternInfo[1];
|
|
int counters[countersLength] = {0,0,0,0,0,0};
|
|
|
|
int lastCode = 0;
|
|
int code = 0;
|
|
int checksumTotal = startCode;
|
|
int multiplier = 0;
|
|
bool lastCharacterWasPrintable = true;
|
|
|
|
while (!done) {
|
|
bool unshift = isNextShifted;
|
|
isNextShifted = false;
|
|
|
|
// Save off last code
|
|
lastCode = code;
|
|
|
|
// Decode another code from image
|
|
try {
|
|
code = decodeCode(row, counters, sizeof(counters)/sizeof(int), nextStart);
|
|
} catch (ReaderException const& re) {
|
|
throw re;
|
|
}
|
|
|
|
// Remember whether the last code was printable or not (excluding CODE_STOP)
|
|
if (code != CODE_STOP) {
|
|
lastCharacterWasPrintable = true;
|
|
}
|
|
|
|
// Add to checksum computation (if not CODE_STOP of course)
|
|
if (code != CODE_STOP) {
|
|
multiplier++;
|
|
checksumTotal += multiplier * code;
|
|
}
|
|
|
|
// Advance to where the next code will to start
|
|
lastStart = nextStart;
|
|
int _countersLength = sizeof(counters) / sizeof(int);
|
|
for (int i = 0; i < _countersLength; i++) {
|
|
nextStart += counters[i];
|
|
}
|
|
|
|
// Take care of illegal start codes
|
|
switch (code) {
|
|
case CODE_START_A:
|
|
case CODE_START_B:
|
|
case CODE_START_C:
|
|
throw ReaderException("");
|
|
}
|
|
|
|
switch (codeSet) {
|
|
|
|
case CODE_CODE_A:
|
|
if (code < 64) {
|
|
tmpResultString.append(1, (char) (' ' + code));
|
|
} else if (code < 96) {
|
|
tmpResultString.append(1, (char) (code - 64));
|
|
} else {
|
|
// Don't let CODE_STOP, which always appears, affect whether whether we think the
|
|
// last code was printable or not.
|
|
if (code != CODE_STOP) {
|
|
lastCharacterWasPrintable = false;
|
|
}
|
|
switch (code) {
|
|
case CODE_FNC_1:
|
|
case CODE_FNC_2:
|
|
case CODE_FNC_3:
|
|
case CODE_FNC_4_A:
|
|
// do nothing?
|
|
break;
|
|
case CODE_SHIFT:
|
|
isNextShifted = true;
|
|
codeSet = CODE_CODE_B;
|
|
break;
|
|
case CODE_CODE_B:
|
|
codeSet = CODE_CODE_B;
|
|
break;
|
|
case CODE_CODE_C:
|
|
codeSet = CODE_CODE_C;
|
|
break;
|
|
case CODE_STOP:
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case CODE_CODE_B:
|
|
if (code < 96) {
|
|
tmpResultString.append(1, (char) (' ' + code));
|
|
} else {
|
|
if (code != CODE_STOP) {
|
|
lastCharacterWasPrintable = false;
|
|
}
|
|
switch (code) {
|
|
case CODE_FNC_1:
|
|
case CODE_FNC_2:
|
|
case CODE_FNC_3:
|
|
case CODE_FNC_4_B:
|
|
// do nothing?
|
|
break;
|
|
case CODE_SHIFT:
|
|
isNextShifted = true;
|
|
codeSet = CODE_CODE_C;
|
|
break;
|
|
case CODE_CODE_A:
|
|
codeSet = CODE_CODE_A;
|
|
break;
|
|
case CODE_CODE_C:
|
|
codeSet = CODE_CODE_C;
|
|
break;
|
|
case CODE_STOP:
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case CODE_CODE_C:
|
|
tmpResultSStr.str(std::string());
|
|
// the code read in this case is the number encoded directly
|
|
if (code < 100) {
|
|
if (code < 10) {
|
|
tmpResultSStr << '0';
|
|
}
|
|
tmpResultSStr << code;
|
|
tmpResultString.append(tmpResultSStr.str());
|
|
} else {
|
|
if (code != CODE_STOP) {
|
|
lastCharacterWasPrintable = false;
|
|
}
|
|
switch (code) {
|
|
case CODE_FNC_1:
|
|
// do nothing?
|
|
break;
|
|
case CODE_CODE_A:
|
|
codeSet = CODE_CODE_A;
|
|
break;
|
|
case CODE_CODE_B:
|
|
codeSet = CODE_CODE_B;
|
|
break;
|
|
case CODE_STOP:
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Unshift back to another code set if we were shifted
|
|
if (unshift) {
|
|
switch (codeSet) {
|
|
case CODE_CODE_A:
|
|
codeSet = CODE_CODE_C;
|
|
break;
|
|
case CODE_CODE_B:
|
|
codeSet = CODE_CODE_A;
|
|
break;
|
|
case CODE_CODE_C:
|
|
codeSet = CODE_CODE_B;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Check for ample whitespace following pattern, but, to do this we first need to remember that
|
|
// we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left
|
|
// to read off. Would be slightly better to properly read. Here we just skip it:
|
|
int width = row->getSize();
|
|
while (nextStart < width && row->get(nextStart)) {
|
|
nextStart++;
|
|
}
|
|
if (!row->isRange(nextStart,
|
|
std::min(width, nextStart + (nextStart - lastStart) / 2),
|
|
false)) {
|
|
throw ReaderException("");
|
|
}
|
|
|
|
// Pull out from sum the value of the penultimate check code
|
|
checksumTotal -= multiplier * lastCode;
|
|
// lastCode is the checksum then:
|
|
if (checksumTotal % 103 != lastCode) {
|
|
throw ReaderException("");
|
|
}
|
|
|
|
// Need to pull out the check digits from string
|
|
int resultLength = tmpResultString.length();
|
|
// Only bother if the result had at least one character, and if the checksum digit happened to
|
|
// be a printable character. If it was just interpreted as a control code, nothing to remove.
|
|
if (resultLength > 0 && lastCharacterWasPrintable) {
|
|
if (codeSet == CODE_CODE_C) {
|
|
tmpResultString.erase(resultLength - 2, resultLength);
|
|
} else {
|
|
tmpResultString.erase(resultLength - 1, resultLength);
|
|
}
|
|
}
|
|
|
|
Ref<String> resultString(new String(tmpResultString));
|
|
if (tmpResultString.length() == 0) {
|
|
// Almost surely a false positive
|
|
throw ReaderException("");
|
|
}
|
|
|
|
float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;
|
|
float right = (float) (nextStart + lastStart) / 2.0f;
|
|
|
|
std::vector< Ref<ResultPoint> > resultPoints(2);
|
|
Ref<OneDResultPoint> resultPoint1(new OneDResultPoint(left, (float) rowNumber));
|
|
Ref<OneDResultPoint> resultPoint2(new OneDResultPoint(right, (float) rowNumber));
|
|
resultPoints[0] = resultPoint1;
|
|
resultPoints[1] = resultPoint2;
|
|
|
|
delete [] startPatternInfo;
|
|
ArrayRef<unsigned char> resultBytes(1);
|
|
return Ref<Result>(new Result(resultString, resultBytes, resultPoints,
|
|
BarcodeFormat_CODE_128));
|
|
} catch (ReaderException const& re) {
|
|
delete [] startPatternInfo;
|
|
return Ref<Result>();
|
|
}
|
|
}
|
|
|
|
void Code128Reader::append(char* s, char c){
|
|
int len = strlen(s);
|
|
s[len] = c;
|
|
s[len + 1] = '\0';
|
|
}
|
|
|
|
Code128Reader::~Code128Reader(){
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/Code39Reader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2010 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 "Code39Reader.h"
|
|
// #include <zxing/oned/OneDResultPoint.h>
|
|
// #include <zxing/common/Array.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <math.h>
|
|
// #include <limits.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
|
|
static const char* ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";
|
|
|
|
|
|
/**
|
|
* These represent the encodings of characters, as patterns of wide and narrow
|
|
* bars.
|
|
* The 9 least-significant bits of each int correspond to the pattern of wide
|
|
* and narrow, with 1s representing "wide" and 0s representing narrow.
|
|
*/
|
|
const int CHARACTER_ENCODINGS_LEN = 44;
|
|
static int CHARACTER_ENCODINGS[CHARACTER_ENCODINGS_LEN] = {
|
|
0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9
|
|
0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J
|
|
0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T
|
|
0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, // U-*
|
|
0x0A8, 0x0A2, 0x08A, 0x02A // $-%
|
|
};
|
|
|
|
static int ASTERISK_ENCODING = 0x094;
|
|
static const char* ALPHABET_STRING =
|
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";
|
|
|
|
|
|
/**
|
|
* Creates a reader that assumes all encoded data is data, and does not treat
|
|
* the final character as a check digit. It will not decoded "extended
|
|
* Code 39" sequences.
|
|
*/
|
|
Code39Reader::Code39Reader() : alphabet_string(ALPHABET_STRING),
|
|
usingCheckDigit(false),
|
|
extendedMode(false) {
|
|
}
|
|
|
|
/**
|
|
* Creates a reader that can be configured to check the last character as a
|
|
* check digit. It will not decoded "extended Code 39" sequences.
|
|
*
|
|
* @param usingCheckDigit if true, treat the last data character as a check
|
|
* digit, not data, and verify that the checksum passes.
|
|
*/
|
|
Code39Reader::Code39Reader(bool usingCheckDigit_) :
|
|
alphabet_string(ALPHABET_STRING),
|
|
usingCheckDigit(usingCheckDigit_),
|
|
extendedMode(false) {
|
|
}
|
|
|
|
|
|
Code39Reader::Code39Reader(bool usingCheckDigit_, bool extendedMode_) :
|
|
alphabet_string(ALPHABET_STRING),
|
|
usingCheckDigit(usingCheckDigit_),
|
|
extendedMode(extendedMode_) {
|
|
}
|
|
|
|
Ref<Result> Code39Reader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
|
int* start = NULL;
|
|
try {
|
|
start = findAsteriskPattern(row);
|
|
int nextStart = start[1];
|
|
int end = row->getSize();
|
|
|
|
// Read off white space
|
|
while (nextStart < end && !row->get(nextStart)) {
|
|
nextStart++;
|
|
}
|
|
|
|
std::string tmpResultString;
|
|
|
|
const int countersLen = 9;
|
|
int counters[countersLen];
|
|
for (int i = 0; i < countersLen; i++) {
|
|
counters[i] = 0;
|
|
}
|
|
char decodedChar;
|
|
int lastStart;
|
|
do {
|
|
if (!recordPattern(row, nextStart, counters, countersLen)) {
|
|
throw ReaderException("");
|
|
}
|
|
int pattern = toNarrowWidePattern(counters, countersLen);
|
|
if (pattern < 0) {
|
|
throw ReaderException("pattern < 0");
|
|
}
|
|
decodedChar = patternToChar(pattern);
|
|
tmpResultString.append(1, decodedChar);
|
|
lastStart = nextStart;
|
|
for (int i = 0; i < countersLen; i++) {
|
|
nextStart += counters[i];
|
|
}
|
|
// Read off white space
|
|
while (nextStart < end && !row->get(nextStart)) {
|
|
nextStart++;
|
|
}
|
|
} while (decodedChar != '*');
|
|
tmpResultString.erase(tmpResultString.length()-1, 1);// remove asterisk
|
|
|
|
// Look for whitespace after pattern:
|
|
int lastPatternSize = 0;
|
|
for (int i = 0; i < countersLen; i++) {
|
|
lastPatternSize += counters[i];
|
|
}
|
|
int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;
|
|
// If 50% of last pattern size, following last pattern, is not whitespace,
|
|
// fail (but if it's whitespace to the very end of the image, that's OK)
|
|
if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize) {
|
|
throw ReaderException("too short end white space");
|
|
}
|
|
|
|
if (usingCheckDigit) {
|
|
int max = tmpResultString.length() - 1;
|
|
unsigned int total = 0;
|
|
for (int i = 0; i < max; i++) {
|
|
total += alphabet_string.find_first_of(tmpResultString[i], 0);
|
|
}
|
|
if (total % 43 != alphabet_string.find_first_of(tmpResultString[max], 0)) {
|
|
throw ReaderException("");
|
|
}
|
|
tmpResultString.erase(max, 1);
|
|
}
|
|
|
|
Ref<String> resultString(new String(tmpResultString));
|
|
if (extendedMode) {
|
|
resultString = decodeExtended(tmpResultString);
|
|
}
|
|
|
|
if (tmpResultString.length() == 0) {
|
|
// Almost surely a false positive
|
|
throw ReaderException("");
|
|
}
|
|
|
|
float left = (float) (start[1] + start[0]) / 2.0f;
|
|
float right = (float) (nextStart + lastStart) / 2.0f;
|
|
|
|
std::vector< Ref<ResultPoint> > resultPoints(2);
|
|
Ref<OneDResultPoint> resultPoint1(
|
|
new OneDResultPoint(left, (float) rowNumber));
|
|
Ref<OneDResultPoint> resultPoint2(
|
|
new OneDResultPoint(right, (float) rowNumber));
|
|
resultPoints[0] = resultPoint1;
|
|
resultPoints[1] = resultPoint2;
|
|
|
|
ArrayRef<unsigned char> resultBytes(1);
|
|
|
|
Ref<Result> res(new Result(
|
|
resultString, resultBytes, resultPoints, BarcodeFormat_CODE_39));
|
|
|
|
delete [] start;
|
|
return res;
|
|
} catch (ReaderException const& re) {
|
|
delete [] start;
|
|
return Ref<Result>();
|
|
}
|
|
}
|
|
|
|
int* Code39Reader::findAsteriskPattern(Ref<BitArray> row){
|
|
int width = row->getSize();
|
|
int rowOffset = 0;
|
|
while (rowOffset < width) {
|
|
if (row->get(rowOffset)) {
|
|
break;
|
|
}
|
|
rowOffset++;
|
|
}
|
|
|
|
int counterPosition = 0;
|
|
const int countersLen = 9;
|
|
int counters[countersLen];
|
|
for (int i = 0; i < countersLen; i++) {
|
|
counters[i] = 0;
|
|
}
|
|
int patternStart = rowOffset;
|
|
bool isWhite = false;
|
|
int patternLength = countersLen;
|
|
|
|
for (int i = rowOffset; i < width; i++) {
|
|
bool pixel = row->get(i);
|
|
if (pixel ^ isWhite) {
|
|
counters[counterPosition]++;
|
|
} else {
|
|
if (counterPosition == patternLength - 1) {
|
|
if (toNarrowWidePattern(counters, countersLen) == ASTERISK_ENCODING) {
|
|
// Look for whitespace before start pattern, >= 50% of width of
|
|
// start pattern.
|
|
if (row->isRange(std::max(0, patternStart - ((i - patternStart) >> 1)), patternStart, false)) {
|
|
int* resultValue = new int[2];
|
|
resultValue[0] = patternStart;
|
|
resultValue[1] = i;
|
|
return resultValue;
|
|
}
|
|
}
|
|
patternStart += counters[0] + counters[1];
|
|
for (int y = 2; y < patternLength; y++) {
|
|
counters[y - 2] = counters[y];
|
|
}
|
|
counters[patternLength - 2] = 0;
|
|
counters[patternLength - 1] = 0;
|
|
counterPosition--;
|
|
} else {
|
|
counterPosition++;
|
|
}
|
|
counters[counterPosition] = 1;
|
|
isWhite = !isWhite;
|
|
}
|
|
}
|
|
throw ReaderException("");
|
|
}
|
|
|
|
// For efficiency, returns -1 on failure. Not throwing here saved as many as
|
|
// 700 exceptions per image when using some of our blackbox images.
|
|
int Code39Reader::toNarrowWidePattern(int counters[], int countersLen){
|
|
int numCounters = countersLen;
|
|
int maxNarrowCounter = 0;
|
|
int wideCounters;
|
|
do {
|
|
int minCounter = INT_MAX;
|
|
for (int i = 0; i < numCounters; i++) {
|
|
int counter = counters[i];
|
|
if (counter < minCounter && counter > maxNarrowCounter) {
|
|
minCounter = counter;
|
|
}
|
|
}
|
|
maxNarrowCounter = minCounter;
|
|
wideCounters = 0;
|
|
int totalWideCountersWidth = 0;
|
|
int pattern = 0;
|
|
for (int i = 0; i < numCounters; i++) {
|
|
int counter = counters[i];
|
|
if (counters[i] > maxNarrowCounter) {
|
|
pattern |= 1 << (numCounters - 1 - i);
|
|
wideCounters++;
|
|
totalWideCountersWidth += counter;
|
|
}
|
|
}
|
|
if (wideCounters == 3) {
|
|
// Found 3 wide counters, but are they close enough in width?
|
|
// We can perform a cheap, conservative check to see if any individual
|
|
// counter is more than 1.5 times the average:
|
|
for (int i = 0; i < numCounters && wideCounters > 0; i++) {
|
|
int counter = counters[i];
|
|
if (counters[i] > maxNarrowCounter) {
|
|
wideCounters--;
|
|
// totalWideCountersWidth = 3 * average, so this checks if
|
|
// counter >= 3/2 * average.
|
|
if ((counter << 1) >= totalWideCountersWidth) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
return pattern;
|
|
}
|
|
} while (wideCounters > 3);
|
|
return -1;
|
|
}
|
|
|
|
char Code39Reader::patternToChar(int pattern){
|
|
for (int i = 0; i < CHARACTER_ENCODINGS_LEN; i++) {
|
|
if (CHARACTER_ENCODINGS[i] == pattern) {
|
|
return ALPHABET[i];
|
|
}
|
|
}
|
|
throw ReaderException("");
|
|
}
|
|
|
|
Ref<String> Code39Reader::decodeExtended(std::string encoded){
|
|
int length = encoded.length();
|
|
std::string tmpDecoded;
|
|
for (int i = 0; i < length; i++) {
|
|
char c = encoded[i];
|
|
if (c == '+' || c == '$' || c == '%' || c == '/') {
|
|
char next = encoded[i + 1];
|
|
char decodedChar = '\0';
|
|
switch (c) {
|
|
case '+':
|
|
// +A to +Z map to a to z
|
|
if (next >= 'A' && next <= 'Z') {
|
|
decodedChar = (char) (next + 32);
|
|
} else {
|
|
throw ReaderException("");
|
|
}
|
|
break;
|
|
case '$':
|
|
// $A to $Z map to control codes SH to SB
|
|
if (next >= 'A' && next <= 'Z') {
|
|
decodedChar = (char) (next - 64);
|
|
} else {
|
|
throw ReaderException("");
|
|
}
|
|
break;
|
|
case '%':
|
|
// %A to %E map to control codes ESC to US
|
|
if (next >= 'A' && next <= 'E') {
|
|
decodedChar = (char) (next - 38);
|
|
} else if (next >= 'F' && next <= 'W') {
|
|
decodedChar = (char) (next - 11);
|
|
} else {
|
|
throw ReaderException("");
|
|
}
|
|
break;
|
|
case '/':
|
|
// /A to /O map to ! to , and /Z maps to :
|
|
if (next >= 'A' && next <= 'O') {
|
|
decodedChar = (char) (next - 32);
|
|
} else if (next == 'Z') {
|
|
decodedChar = ':';
|
|
} else {
|
|
throw ReaderException("");
|
|
}
|
|
break;
|
|
}
|
|
tmpDecoded.append(1, decodedChar);
|
|
// bump up i again since we read two characters
|
|
i++;
|
|
} else {
|
|
tmpDecoded.append(1, c);
|
|
}
|
|
}
|
|
Ref<String> decoded(new String(tmpDecoded));
|
|
return decoded;
|
|
}
|
|
} // namespace oned
|
|
} // namespace zxing
|
|
|
|
|
|
// file: zxing/oned/EAN13Reader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2010 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 "EAN13Reader.h"
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
|
|
static const int FIRST_DIGIT_ENCODINGS[10] = {
|
|
0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A
|
|
};
|
|
|
|
EAN13Reader::EAN13Reader() { }
|
|
|
|
int EAN13Reader::decodeMiddle(Ref<BitArray> row, int startGuardBegin, int startGuardEnd,
|
|
std::string& resultString) {
|
|
(void)startGuardBegin;
|
|
const int countersLen = 4;
|
|
int counters[countersLen] = { 0, 0, 0, 0 };
|
|
|
|
int end = row->getSize();
|
|
int rowOffset = startGuardEnd;
|
|
int lgPatternFound = 0;
|
|
|
|
for (int x = 0; x < 6 && rowOffset < end; x++) {
|
|
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset,
|
|
UPC_EAN_PATTERNS_L_AND_G_PATTERNS);
|
|
if (bestMatch < 0) {
|
|
return -1;
|
|
}
|
|
resultString.append(1, (char) ('0' + bestMatch % 10));
|
|
for (int i = 0; i < countersLen; i++) {
|
|
rowOffset += counters[i];
|
|
}
|
|
if (bestMatch >= 10) {
|
|
lgPatternFound |= 1 << (5 - x);
|
|
}
|
|
}
|
|
|
|
if (!determineFirstDigit(resultString, lgPatternFound)) {
|
|
return -1;
|
|
}
|
|
|
|
int middleRangeStart;
|
|
int middleRangeEnd;
|
|
if (findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(),
|
|
getMIDDLE_PATTERN_LEN(), &middleRangeStart, &middleRangeEnd)) {
|
|
rowOffset = middleRangeEnd;
|
|
for (int x = 0; x < 6 && rowOffset < end; x++) {
|
|
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset,
|
|
UPC_EAN_PATTERNS_L_PATTERNS);
|
|
if (bestMatch < 0) {
|
|
return -1;
|
|
}
|
|
resultString.append(1, (char) ('0' + bestMatch));
|
|
for (int i = 0; i < countersLen; i++) {
|
|
rowOffset += counters[i];
|
|
}
|
|
}
|
|
return rowOffset;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool EAN13Reader::determineFirstDigit(std::string& resultString, int lgPatternFound) {
|
|
for (int d = 0; d < 10; d++) {
|
|
if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) {
|
|
resultString.insert(0, 1, (char) ('0' + d));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
BarcodeFormat EAN13Reader::getBarcodeFormat(){
|
|
return BarcodeFormat_EAN_13;
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/EAN8Reader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2010 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 "EAN8Reader.h"
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
|
|
EAN8Reader::EAN8Reader(){ }
|
|
|
|
int EAN8Reader::decodeMiddle(Ref<BitArray> row, int startGuardBegin, int startGuardEnd,
|
|
std::string& resultString){
|
|
(void)startGuardBegin;
|
|
const int countersLen = 4;
|
|
int counters[countersLen] = { 0, 0, 0, 0 };
|
|
|
|
int end = row->getSize();
|
|
int rowOffset = startGuardEnd;
|
|
|
|
for (int x = 0; x < 4 && rowOffset < end; x++) {
|
|
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset,
|
|
UPC_EAN_PATTERNS_L_PATTERNS);
|
|
if (bestMatch < 0) {
|
|
return -1;
|
|
}
|
|
resultString.append(1, (char) ('0' + bestMatch));
|
|
for (int i = 0; i < countersLen; i++) {
|
|
rowOffset += counters[i];
|
|
}
|
|
}
|
|
|
|
int middleRangeStart;
|
|
int middleRangeEnd;
|
|
if (findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(),
|
|
getMIDDLE_PATTERN_LEN(), &middleRangeStart, &middleRangeEnd)) {
|
|
rowOffset = middleRangeEnd;
|
|
for (int x = 0; x < 4 && rowOffset < end; x++) {
|
|
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset,
|
|
UPC_EAN_PATTERNS_L_PATTERNS);
|
|
if (bestMatch < 0) {
|
|
return -1;
|
|
}
|
|
resultString.append(1, (char) ('0' + bestMatch));
|
|
for (int i = 0; i < countersLen; i++) {
|
|
rowOffset += counters[i];
|
|
}
|
|
}
|
|
return rowOffset;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
BarcodeFormat EAN8Reader::getBarcodeFormat(){
|
|
return BarcodeFormat_EAN_8;
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/ITFReader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2010 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 "ITFReader.h"
|
|
// #include <zxing/oned/OneDResultPoint.h>
|
|
// #include <zxing/common/Array.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <math.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
|
|
static const int W = 3; // Pixel width of a wide line
|
|
static const int N = 1; // Pixed width of a narrow line
|
|
|
|
const int DEFAULT_ALLOWED_LENGTHS_LEN = 10;
|
|
const int DEFAULT_ALLOWED_LENGTHS[DEFAULT_ALLOWED_LENGTHS_LEN] = { 44, 24, 20, 18, 16, 14, 12, 10, 8, 6 };
|
|
|
|
/**
|
|
* Start/end guard pattern.
|
|
*
|
|
* Note: The end pattern is reversed because the row is reversed before
|
|
* searching for the END_PATTERN
|
|
*/
|
|
static const int START_PATTERN_LEN = 4;
|
|
static const int START_PATTERN[START_PATTERN_LEN] = {N, N, N, N};
|
|
|
|
static const int END_PATTERN_REVERSED_LEN = 3;
|
|
static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W};
|
|
|
|
/**
|
|
* Patterns of Wide / Narrow lines to indicate each digit
|
|
*/
|
|
static const int PATTERNS_LEN = 10;
|
|
static const int PATTERNS[PATTERNS_LEN][5] = {
|
|
{N, N, W, W, N}, // 0
|
|
{W, N, N, N, W}, // 1
|
|
{N, W, N, N, W}, // 2
|
|
{W, W, N, N, N}, // 3
|
|
{N, N, W, N, W}, // 4
|
|
{W, N, W, N, N}, // 5
|
|
{N, W, W, N, N}, // 6
|
|
{N, N, N, W, W}, // 7
|
|
{W, N, N, W, N}, // 8
|
|
{N, W, N, W, N} // 9
|
|
};
|
|
|
|
|
|
ITFReader::ITFReader() : narrowLineWidth(-1) {
|
|
}
|
|
|
|
|
|
Ref<Result> ITFReader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
|
int* startRange = 0;
|
|
int* endRange = 0;
|
|
try {
|
|
// Find out where the Middle section (payload) starts & ends
|
|
startRange = decodeStart(row);
|
|
endRange = decodeEnd(row);
|
|
|
|
std::string tmpResult;
|
|
decodeMiddle(row, startRange[1], endRange[0], tmpResult);
|
|
|
|
// To avoid false positives with 2D barcodes (and other patterns), make
|
|
// an assumption that the decoded string must be a known length
|
|
int length = tmpResult.length();
|
|
bool lengthOK = false;
|
|
for (int i = 0; i < DEFAULT_ALLOWED_LENGTHS_LEN; i++) {
|
|
if (length == DEFAULT_ALLOWED_LENGTHS[i]) {
|
|
lengthOK = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!lengthOK) {
|
|
throw ReaderException("not enough characters count");
|
|
}
|
|
|
|
Ref<String> resultString(new String(tmpResult));
|
|
|
|
std::vector< Ref<ResultPoint> > resultPoints(2);
|
|
Ref<OneDResultPoint> resultPoint1(new OneDResultPoint(startRange[1], (float) rowNumber));
|
|
Ref<OneDResultPoint> resultPoint2(new OneDResultPoint(endRange[0], (float) rowNumber));
|
|
resultPoints[0] = resultPoint1;
|
|
resultPoints[1] = resultPoint2;
|
|
|
|
delete [] startRange;
|
|
delete [] endRange;
|
|
ArrayRef<unsigned char> resultBytes(1);
|
|
return Ref<Result>(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF));
|
|
} catch (ReaderException const& re) {
|
|
delete [] startRange;
|
|
delete [] endRange;
|
|
return Ref<Result>();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param row row of black/white values to search
|
|
* @param payloadStart offset of start pattern
|
|
* @param resultString {@link StringBuffer} to append decoded chars to
|
|
* @throws ReaderException if decoding could not complete successfully
|
|
*/
|
|
void ITFReader::decodeMiddle(Ref<BitArray> row, int payloadStart, int payloadEnd,
|
|
std::string& resultString) {
|
|
// Digits are interleaved in pairs - 5 black lines for one digit, and the
|
|
// 5
|
|
// interleaved white lines for the second digit.
|
|
// Therefore, need to scan 10 lines and then
|
|
// split these into two arrays
|
|
int counterDigitPairLen = 10;
|
|
int counterDigitPair[counterDigitPairLen];
|
|
for (int i=0; i<counterDigitPairLen; i++) {
|
|
counterDigitPair[i] = 0;
|
|
}
|
|
|
|
int counterBlack[5];
|
|
int counterWhite[5];
|
|
for (int i=0; i<5; i++) {
|
|
counterBlack[i] = 0;
|
|
counterWhite[i] = 0;
|
|
}
|
|
|
|
while (payloadStart < payloadEnd) {
|
|
// Get 10 runs of black/white.
|
|
if (!recordPattern(row, payloadStart, counterDigitPair, counterDigitPairLen)) {
|
|
throw ReaderException("");
|
|
}
|
|
// Split them into each array
|
|
for (int k = 0; k < 5; k++) {
|
|
int twoK = k << 1;
|
|
counterBlack[k] = counterDigitPair[twoK];
|
|
counterWhite[k] = counterDigitPair[twoK + 1];
|
|
}
|
|
|
|
int bestMatch = decodeDigit(counterBlack, 5);
|
|
resultString.append(1, (char) ('0' + bestMatch));
|
|
bestMatch = decodeDigit(counterWhite, 5);
|
|
resultString.append(1, (char) ('0' + bestMatch));
|
|
|
|
for (int i = 0; i < counterDigitPairLen; i++) {
|
|
payloadStart += counterDigitPair[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Identify where the start of the middle / payload section starts.
|
|
*
|
|
* @param row row of black/white values to search
|
|
* @return Array, containing index of start of 'start block' and end of
|
|
* 'start block'
|
|
* @throws ReaderException
|
|
*/
|
|
int* ITFReader::decodeStart(Ref<BitArray> row) {
|
|
int endStart = skipWhiteSpace(row);
|
|
int* startPattern = 0;
|
|
try {
|
|
startPattern = findGuardPattern(row, endStart, START_PATTERN, START_PATTERN_LEN);
|
|
|
|
// Determine the width of a narrow line in pixels. We can do this by
|
|
// getting the width of the start pattern and dividing by 4 because its
|
|
// made up of 4 narrow lines.
|
|
narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2;
|
|
validateQuietZone(row, startPattern[0]);
|
|
return startPattern;
|
|
} catch (ReaderException const& re) {
|
|
delete [] startPattern;
|
|
throw re;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Identify where the end of the middle / payload section ends.
|
|
*
|
|
* @param row row of black/white values to search
|
|
* @return Array, containing index of start of 'end block' and end of 'end
|
|
* block'
|
|
* @throws ReaderException
|
|
*/
|
|
|
|
int* ITFReader::decodeEnd(Ref<BitArray> row) {
|
|
// For convenience, reverse the row and then
|
|
// search from 'the start' for the end block
|
|
row->reverse();
|
|
int* endPattern = 0;
|
|
try {
|
|
int endStart = skipWhiteSpace(row);
|
|
endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN);
|
|
|
|
// The start & end patterns must be pre/post fixed by a quiet zone. This
|
|
// zone must be at least 10 times the width of a narrow line.
|
|
// ref: http://www.barcode-1.net/i25code.html
|
|
validateQuietZone(row, endPattern[0]);
|
|
|
|
// Now recalculate the indices of where the 'endblock' starts & stops to
|
|
// accommodate
|
|
// the reversed nature of the search
|
|
int temp = endPattern[0];
|
|
endPattern[0] = row->getSize() - endPattern[1];
|
|
endPattern[1] = row->getSize() - temp;
|
|
|
|
row->reverse();
|
|
return endPattern;
|
|
} catch (ReaderException const& re) {
|
|
delete [] endPattern;
|
|
row->reverse();
|
|
throw re;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The start & end patterns must be pre/post fixed by a quiet zone. This
|
|
* zone must be at least 10 times the width of a narrow line. Scan back until
|
|
* we either get to the start of the barcode or match the necessary number of
|
|
* quiet zone pixels.
|
|
*
|
|
* Note: Its assumed the row is reversed when using this method to find
|
|
* quiet zone after the end pattern.
|
|
*
|
|
* ref: http://www.barcode-1.net/i25code.html
|
|
*
|
|
* @param row bit array representing the scanned barcode.
|
|
* @param startPattern index into row of the start or end pattern.
|
|
* @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown.
|
|
*/
|
|
void ITFReader::validateQuietZone(Ref<BitArray> row, int startPattern) {
|
|
(void)row;
|
|
(void)startPattern;
|
|
//#pragma mark needs some corrections
|
|
// int quietCount = narrowLineWidth * 10; // expect to find this many pixels of quiet zone
|
|
//
|
|
// for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {
|
|
// if (row->get(i)) {
|
|
// break;
|
|
// }
|
|
// quietCount--;
|
|
// }
|
|
// if (quietCount != 0) {
|
|
// // Unable to find the necessary number of quiet zone pixels.
|
|
// throw ReaderException("Unable to find the necessary number of quiet zone pixels");
|
|
// }
|
|
}
|
|
|
|
/**
|
|
* Skip all whitespace until we get to the first black line.
|
|
*
|
|
* @param row row of black/white values to search
|
|
* @return index of the first black line.
|
|
* @throws ReaderException Throws exception if no black lines are found in the row
|
|
*/
|
|
int ITFReader::skipWhiteSpace(Ref<BitArray> row) {
|
|
int width = row->getSize();
|
|
int endStart = 0;
|
|
while (endStart < width) {
|
|
if (row->get(endStart)) {
|
|
break;
|
|
}
|
|
endStart++;
|
|
}
|
|
if (endStart == width) {
|
|
throw ReaderException("");
|
|
}
|
|
return endStart;
|
|
}
|
|
|
|
/**
|
|
* @param row row of black/white values to search
|
|
* @param rowOffset position to start search
|
|
* @param pattern pattern of counts of number of black and white pixels that are
|
|
* being searched for as a pattern
|
|
* @return start/end horizontal offset of guard pattern, as an array of two
|
|
* ints
|
|
* @throws ReaderException if pattern is not found
|
|
*/
|
|
int* ITFReader::findGuardPattern(Ref<BitArray> row, int rowOffset, const int pattern[],
|
|
int patternLen) {
|
|
// TODO: This is very similar to implementation in UPCEANReader. Consider if they can be
|
|
// merged to a single method.
|
|
int patternLength = patternLen;
|
|
int counters[patternLength];
|
|
for (int i=0; i<patternLength; i++) {
|
|
counters[i] = 0;
|
|
}
|
|
int width = row->getSize();
|
|
bool isWhite = false;
|
|
|
|
int counterPosition = 0;
|
|
int patternStart = rowOffset;
|
|
for (int x = rowOffset; x < width; x++) {
|
|
bool pixel = row->get(x);
|
|
if (pixel ^ isWhite) {
|
|
counters[counterPosition]++;
|
|
} else {
|
|
if (counterPosition == patternLength - 1) {
|
|
if (patternMatchVariance(counters, patternLength, pattern,
|
|
MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
|
|
int* resultValue = new int[2];
|
|
resultValue[0] = patternStart;
|
|
resultValue[1] = x;
|
|
return resultValue;
|
|
}
|
|
patternStart += counters[0] + counters[1];
|
|
for (int y = 2; y < patternLength; y++) {
|
|
counters[y - 2] = counters[y];
|
|
}
|
|
counters[patternLength - 2] = 0;
|
|
counters[patternLength - 1] = 0;
|
|
counterPosition--;
|
|
} else {
|
|
counterPosition++;
|
|
}
|
|
counters[counterPosition] = 1;
|
|
isWhite = !isWhite;
|
|
}
|
|
}
|
|
throw ReaderException("");
|
|
}
|
|
|
|
/**
|
|
* Attempts to decode a sequence of ITF black/white lines into single
|
|
* digit.
|
|
*
|
|
* @param counters the counts of runs of observed black/white/black/... values
|
|
* @return The decoded digit
|
|
* @throws ReaderException if digit cannot be decoded
|
|
*/
|
|
int ITFReader::decodeDigit(int counters[], int countersLen){
|
|
unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
|
int bestMatch = -1;
|
|
int max = PATTERNS_LEN;
|
|
for (int i = 0; i < max; i++) {
|
|
int pattern[countersLen];
|
|
for(int ind = 0; ind<countersLen; ind++){
|
|
pattern[ind] = PATTERNS[i][ind];
|
|
}
|
|
unsigned int variance = patternMatchVariance(counters, countersLen, pattern,
|
|
MAX_INDIVIDUAL_VARIANCE);
|
|
if (variance < bestVariance) {
|
|
bestVariance = variance;
|
|
bestMatch = i;
|
|
}
|
|
}
|
|
if (bestMatch >= 0) {
|
|
return bestMatch;
|
|
} else {
|
|
throw ReaderException("digit didint found");
|
|
}
|
|
}
|
|
|
|
ITFReader::~ITFReader(){
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/MultiFormatOneDReader.cpp
|
|
|
|
/*
|
|
* MultiFormatOneDReader.cpp
|
|
* ZXing
|
|
*
|
|
* Copyright 2010 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 "MultiFormatOneDReader.h"
|
|
|
|
// #include <zxing/oned/MultiFormatUPCEANReader.h>
|
|
// #include <zxing/oned/Code39Reader.h>
|
|
// #include <zxing/oned/Code128Reader.h>
|
|
// #include <zxing/oned/ITFReader.h>
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
MultiFormatOneDReader::MultiFormatOneDReader(DecodeHints hints) : readers() {
|
|
if (hints.containsFormat(BarcodeFormat_EAN_13) ||
|
|
hints.containsFormat(BarcodeFormat_EAN_8) ||
|
|
hints.containsFormat(BarcodeFormat_UPC_A) ||
|
|
hints.containsFormat(BarcodeFormat_UPC_E)) {
|
|
readers.push_back(Ref<OneDReader>(new MultiFormatUPCEANReader(hints)));
|
|
}
|
|
if (hints.containsFormat(BarcodeFormat_CODE_39)) {
|
|
readers.push_back(Ref<OneDReader>(new Code39Reader()));
|
|
}
|
|
if (hints.containsFormat(BarcodeFormat_CODE_128)) {
|
|
readers.push_back(Ref<OneDReader>(new Code128Reader()));
|
|
}
|
|
if (hints.containsFormat(BarcodeFormat_ITF)) {
|
|
readers.push_back(Ref<OneDReader>(new ITFReader()));
|
|
}
|
|
if (readers.size() == 0) {
|
|
readers.push_back(Ref<OneDReader>(new MultiFormatUPCEANReader(hints)));
|
|
readers.push_back(Ref<OneDReader>(new Code39Reader()));
|
|
readers.push_back(Ref<OneDReader>(new Code128Reader()));
|
|
readers.push_back(Ref<OneDReader>(new ITFReader()));
|
|
}
|
|
}
|
|
|
|
Ref<Result> MultiFormatOneDReader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
|
int size = readers.size();
|
|
for (int i = 0; i < size; i++) {
|
|
OneDReader* reader = readers[i];
|
|
Ref<Result> result = reader->decodeRow(rowNumber, row);
|
|
if (!result.empty()) {
|
|
return result;
|
|
}
|
|
}
|
|
return Ref<Result>();
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/MultiFormatUPCEANReader.cpp
|
|
|
|
/*
|
|
* MultiFormatUPCEANReader.cpp
|
|
* ZXing
|
|
*
|
|
* Copyright 2010 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 "MultiFormatUPCEANReader.h"
|
|
|
|
// #include <zxing/oned/EAN13Reader.h>
|
|
// #include <zxing/oned/EAN8Reader.h>
|
|
// #include <zxing/oned/UPCEReader.h>
|
|
// #include <zxing/oned/UPCAReader.h>
|
|
// #include <zxing/oned/OneDResultPoint.h>
|
|
// #include <zxing/common/Array.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <math.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
|
|
MultiFormatUPCEANReader::MultiFormatUPCEANReader(DecodeHints hints) : readers() {
|
|
if (hints.containsFormat(BarcodeFormat_EAN_13)) {
|
|
readers.push_back(Ref<OneDReader>(new EAN13Reader()));
|
|
} else if (hints.containsFormat(BarcodeFormat_UPC_A)) {
|
|
readers.push_back(Ref<OneDReader>(new UPCAReader()));
|
|
}
|
|
if (hints.containsFormat(BarcodeFormat_EAN_8)) {
|
|
readers.push_back(Ref<OneDReader>(new EAN8Reader()));
|
|
}
|
|
if (hints.containsFormat(BarcodeFormat_UPC_E)) {
|
|
readers.push_back(Ref<OneDReader>(new UPCEReader()));
|
|
}
|
|
if (readers.size() == 0) {
|
|
readers.push_back(Ref<OneDReader>(new EAN13Reader()));
|
|
// UPC-A is covered by EAN-13
|
|
readers.push_back(Ref<OneDReader>(new EAN8Reader()));
|
|
readers.push_back(Ref<OneDReader>(new UPCEReader()));
|
|
}
|
|
}
|
|
|
|
Ref<Result> MultiFormatUPCEANReader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
|
// Compute this location once and reuse it on multiple implementations
|
|
int size = readers.size();
|
|
for (int i = 0; i < size; i++) {
|
|
Ref<OneDReader> reader = readers[i];
|
|
Ref<Result> result = reader->decodeRow(rowNumber, row);
|
|
if (result.empty()) {
|
|
continue;
|
|
}
|
|
|
|
// Special case: a 12-digit code encoded in UPC-A is identical to a "0"
|
|
// followed by those 12 digits encoded as EAN-13. Each will recognize such a code,
|
|
// UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with "0".
|
|
// Individually these are correct and their readers will both read such a code
|
|
// and correctly call it EAN-13, or UPC-A, respectively.
|
|
//
|
|
// In this case, if we've been looking for both types, we'd like to call it
|
|
// a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read
|
|
// UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A
|
|
// result if appropriate.
|
|
if (result->getBarcodeFormat() == BarcodeFormat_EAN_13) {
|
|
const std::string& text = (result->getText())->getText();
|
|
if (text[0] == '0') {
|
|
Ref<String> resultString(new String(text.substr(1)));
|
|
Ref<Result> res(new Result(resultString, result->getRawBytes(),
|
|
result->getResultPoints(), BarcodeFormat_UPC_A));
|
|
return res;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
return Ref<Result>();
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/OneDReader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* OneDReader.cpp
|
|
* ZXing
|
|
*
|
|
* Copyright 2010 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 "OneDReader.h"
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <zxing/oned/OneDResultPoint.h>
|
|
// #include <math.h>
|
|
// #include <limits.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
using namespace std;
|
|
|
|
OneDReader::OneDReader() {
|
|
}
|
|
|
|
Ref<Result> OneDReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
|
Ref<Result> result = doDecode(image, hints);
|
|
if (result.empty()) { // && hints.getTryHarder() && image->isRotateSupported()) {
|
|
Ref<BinaryBitmap> rotatedImage(image->rotateCounterClockwise());
|
|
result = doDecode(rotatedImage, hints);
|
|
if (!result.empty()) {
|
|
/*
|
|
// Record that we found it rotated 90 degrees CCW / 270 degrees CW
|
|
Hashtable metadata = result.getResultMetadata();
|
|
int orientation = 270;
|
|
if (metadata != null && metadata.containsKey(ResultMetadataType.ORIENTATION)) {
|
|
// But if we found it reversed in doDecode(), add in that result here:
|
|
orientation = (orientation +
|
|
((Integer) metadata.get(ResultMetadataType.ORIENTATION)).intValue()) % 360;
|
|
}
|
|
result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(orientation));
|
|
*/
|
|
// Update result points
|
|
std::vector<Ref<ResultPoint> >& points (result->getResultPoints());
|
|
int height = rotatedImage->getHeight();
|
|
for (size_t i = 0; i < points.size(); i++) {
|
|
points[i].reset(new OneDResultPoint(height - points[i]->getY() - 1, points[i]->getX()));
|
|
}
|
|
}
|
|
}
|
|
if (result.empty()) {
|
|
throw ReaderException("");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Ref<Result> OneDReader::doDecode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
|
int width = image->getWidth();
|
|
int height = image->getHeight();
|
|
Ref<BitArray> row(new BitArray(width));
|
|
int middle = height >> 1;
|
|
bool tryHarder = hints.getTryHarder();
|
|
int rowStep = (int)fmax(1, height >> (tryHarder ? 8 : 5));
|
|
int maxLines;
|
|
if (tryHarder) {
|
|
maxLines = height; // Look at the whole image, not just the center
|
|
} else {
|
|
maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image
|
|
}
|
|
|
|
for (int x = 0; x < maxLines; x++) {
|
|
// Scanning from the middle out. Determine which row we're looking at next:
|
|
int rowStepsAboveOrBelow = (x + 1) >> 1;
|
|
bool isAbove = (x & 0x01) == 0; // i.e. is x even?
|
|
int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);
|
|
if (rowNumber < 0 || rowNumber >= height) {
|
|
// Oops, if we run off the top or bottom, stop
|
|
break;
|
|
}
|
|
|
|
// Estimate black point for this row and load it:
|
|
try {
|
|
row = image->getBlackRow(rowNumber, row);
|
|
} catch (ReaderException const& re) {
|
|
continue;
|
|
} catch (IllegalArgumentException const& re) {
|
|
continue;
|
|
}
|
|
|
|
// While we have the image data in a BitArray, it's fairly cheap to reverse it in place to
|
|
// handle decoding upside down barcodes.
|
|
for (int attempt = 0; attempt < 2; attempt++) {
|
|
if (attempt == 1) {
|
|
row->reverse(); // reverse the row and continue
|
|
}
|
|
|
|
// Look for a barcode
|
|
Ref<Result> result = decodeRow(rowNumber, row);
|
|
// We found our barcode
|
|
if (!result.empty()) {
|
|
if (attempt == 1) {
|
|
// But it was upside down, so note that
|
|
// result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(180));
|
|
// And remember to flip the result points horizontally.
|
|
std::vector<Ref<ResultPoint> > points(result->getResultPoints());
|
|
// if there's exactly two points (which there should be), flip the x coordinate
|
|
// if there's not exactly 2, I don't know what do do with it
|
|
if (points.size() == 2) {
|
|
Ref<ResultPoint> pointZero(new OneDResultPoint(width - points[0]->getX() - 1,
|
|
points[0]->getY()));
|
|
points[0] = pointZero;
|
|
|
|
Ref<ResultPoint> pointOne(new OneDResultPoint(width - points[1]->getX() - 1,
|
|
points[1]->getY()));
|
|
points[1] = pointOne;
|
|
|
|
result.reset(new Result(result->getText(), result->getRawBytes(), points,
|
|
result->getBarcodeFormat()));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return Ref<Result>();
|
|
}
|
|
|
|
unsigned int OneDReader::patternMatchVariance(int counters[], int countersSize,
|
|
const int pattern[], int maxIndividualVariance) {
|
|
int numCounters = countersSize;
|
|
unsigned int total = 0;
|
|
unsigned int patternLength = 0;
|
|
for (int i = 0; i < numCounters; i++) {
|
|
total += counters[i];
|
|
patternLength += pattern[i];
|
|
}
|
|
if (total < patternLength) {
|
|
// If we don't even have one pixel per unit of bar width, assume this is too small
|
|
// to reliably match, so fail:
|
|
return INT_MAX;
|
|
}
|
|
// We're going to fake floating-point math in integers. We just need to use more bits.
|
|
// Scale up patternLength so that intermediate values below like scaledCounter will have
|
|
// more "significant digits"
|
|
unsigned int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
|
|
maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
|
|
|
|
unsigned int totalVariance = 0;
|
|
for (int x = 0; x < numCounters; x++) {
|
|
int counter = counters[x] << INTEGER_MATH_SHIFT;
|
|
int scaledPattern = pattern[x] * unitBarWidth;
|
|
int variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
|
|
if (variance > maxIndividualVariance) {
|
|
return INT_MAX;
|
|
}
|
|
totalVariance += variance;
|
|
}
|
|
return totalVariance / total;
|
|
}
|
|
|
|
bool OneDReader::recordPattern(Ref<BitArray> row, int start, int counters[], int countersCount) {
|
|
int numCounters = countersCount;//sizeof(counters) / sizeof(int);
|
|
for (int i = 0; i < numCounters; i++) {
|
|
counters[i] = 0;
|
|
}
|
|
int end = row->getSize();
|
|
if (start >= end) {
|
|
return false;
|
|
}
|
|
bool isWhite = !row->get(start);
|
|
int counterPosition = 0;
|
|
int i = start;
|
|
while (i < end) {
|
|
bool pixel = row->get(i);
|
|
if (pixel ^ isWhite) { // that is, exactly one is true
|
|
counters[counterPosition]++;
|
|
} else {
|
|
counterPosition++;
|
|
if (counterPosition == numCounters) {
|
|
break;
|
|
} else {
|
|
counters[counterPosition] = 1;
|
|
isWhite ^= true; // isWhite = !isWhite;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
// If we read fully the last section of pixels and filled up our counters -- or filled
|
|
// the last counter but ran off the side of the image, OK. Otherwise, a problem.
|
|
if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
OneDReader::~OneDReader() {
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/OneDResultPoint.cpp
|
|
|
|
/*
|
|
* OneDResultPoint.cpp
|
|
* ZXing
|
|
*
|
|
* Copyright 2010 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 "OneDResultPoint.h"
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
|
|
OneDResultPoint::OneDResultPoint(float posX, float posY) : ResultPoint(posX,posY) {
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/UPCAReader.cpp
|
|
|
|
/*
|
|
* UPCAReader.cpp
|
|
* ZXing
|
|
*
|
|
* Copyright 2010 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 "UPCAReader.h"
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
UPCAReader::UPCAReader() : ean13Reader() {
|
|
}
|
|
|
|
Ref<Result> UPCAReader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
|
return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row));
|
|
}
|
|
|
|
Ref<Result> UPCAReader::decodeRow(int rowNumber, Ref<BitArray> row, int startGuardBegin,
|
|
int startGuardEnd) {
|
|
return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardBegin,
|
|
startGuardEnd));
|
|
}
|
|
|
|
Ref<Result> UPCAReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
|
return maybeReturnResult(ean13Reader.decode(image, hints));
|
|
}
|
|
|
|
int UPCAReader::decodeMiddle(Ref<BitArray> row, int startGuardBegin, int startGuardEnd,
|
|
std::string& resultString) {
|
|
return ean13Reader.decodeMiddle(row, startGuardBegin, startGuardEnd, resultString);
|
|
}
|
|
|
|
Ref<Result> UPCAReader::maybeReturnResult(Ref<Result> result) {
|
|
if (result.empty()) {
|
|
return result;
|
|
}
|
|
const std::string& text = (result->getText())->getText();
|
|
if (text[0] == '0') {
|
|
Ref<String> resultString(new String(text.substr(1)));
|
|
Ref<Result> res(new Result(resultString, result->getRawBytes(), result->getResultPoints(),
|
|
BarcodeFormat_UPC_A));
|
|
return res;
|
|
}
|
|
return Ref<Result>();
|
|
}
|
|
|
|
BarcodeFormat UPCAReader::getBarcodeFormat(){
|
|
return BarcodeFormat_UPC_A;
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/UPCEANReader.cpp
|
|
|
|
/*
|
|
* UPCEANReader.cpp
|
|
* ZXing
|
|
*
|
|
* Copyright 2010 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 "UPCEANReader.h"
|
|
// #include <zxing/oned/OneDResultPoint.h>
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
|
|
/**
|
|
* Start/end guard pattern.
|
|
*/
|
|
static const int START_END_PATTERN[3] = {1, 1, 1};
|
|
|
|
/**
|
|
* Pattern marking the middle of a UPC/EAN pattern, separating the two halves.
|
|
*/
|
|
static const int MIDDLE_PATTERN_LEN = 5;
|
|
static const int MIDDLE_PATTERN[MIDDLE_PATTERN_LEN] = {1, 1, 1, 1, 1};
|
|
|
|
/**
|
|
* "Odd", or "L" patterns used to encode UPC/EAN digits.
|
|
*/
|
|
const int L_PATTERNS_LEN = 10;
|
|
const int L_PATTERNS_SUB_LEN = 4;
|
|
const int L_PATTERNS[L_PATTERNS_LEN][L_PATTERNS_SUB_LEN] = {
|
|
{3, 2, 1, 1}, // 0
|
|
{2, 2, 2, 1}, // 1
|
|
{2, 1, 2, 2}, // 2
|
|
{1, 4, 1, 1}, // 3
|
|
{1, 1, 3, 2}, // 4
|
|
{1, 2, 3, 1}, // 5
|
|
{1, 1, 1, 4}, // 6
|
|
{1, 3, 1, 2}, // 7
|
|
{1, 2, 1, 3}, // 8
|
|
{3, 1, 1, 2} // 9
|
|
};
|
|
|
|
/**
|
|
* As above but also including the "even", or "G" patterns used to encode UPC/EAN digits.
|
|
*/
|
|
const int L_AND_G_PATTERNS_LEN = 20;
|
|
const int L_AND_G_PATTERNS_SUB_LEN = 4;
|
|
const int L_AND_G_PATTERNS[L_AND_G_PATTERNS_LEN][L_AND_G_PATTERNS_SUB_LEN] = {
|
|
{3, 2, 1, 1}, // 0
|
|
{2, 2, 2, 1}, // 1
|
|
{2, 1, 2, 2}, // 2
|
|
{1, 4, 1, 1}, // 3
|
|
{1, 1, 3, 2}, // 4
|
|
{1, 2, 3, 1}, // 5
|
|
{1, 1, 1, 4}, // 6
|
|
{1, 3, 1, 2}, // 7
|
|
{1, 2, 1, 3}, // 8
|
|
{3, 1, 1, 2}, // 9
|
|
{1, 1, 2, 3}, // 10 reversed 0
|
|
{1, 2, 2, 2}, // 11 reversed 1
|
|
{2, 2, 1, 2}, // 12 reversed 2
|
|
{1, 1, 4, 1}, // 13 reversed 3
|
|
{2, 3, 1, 1}, // 14 reversed 4
|
|
{1, 3, 2, 1}, // 15 reversed 5
|
|
{4, 1, 1, 1}, // 16 reversed 6
|
|
{2, 1, 3, 1}, // 17 reversed 7
|
|
{3, 1, 2, 1}, // 18 reversed 8
|
|
{2, 1, 1, 3} // 19 reversed 9
|
|
};
|
|
|
|
|
|
int UPCEANReader::getMIDDLE_PATTERN_LEN() {
|
|
return MIDDLE_PATTERN_LEN;
|
|
}
|
|
|
|
const int* UPCEANReader::getMIDDLE_PATTERN() {
|
|
return MIDDLE_PATTERN;
|
|
}
|
|
|
|
UPCEANReader::UPCEANReader() {
|
|
}
|
|
|
|
|
|
Ref<Result> UPCEANReader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
|
int rangeStart;
|
|
int rangeEnd;
|
|
if (findStartGuardPattern(row, &rangeStart, &rangeEnd)) {
|
|
try {
|
|
return decodeRow(rowNumber, row, rangeStart, rangeEnd);
|
|
} catch (ReaderException const& re) {
|
|
}
|
|
}
|
|
return Ref<Result>();
|
|
}
|
|
|
|
Ref<Result> UPCEANReader::decodeRow(int rowNumber, Ref<BitArray> row, int startGuardBegin,
|
|
int startGuardEnd) {
|
|
std::string tmpResultString;
|
|
std::string& tmpResultStringRef = tmpResultString;
|
|
int endStart = decodeMiddle(row, startGuardBegin, startGuardEnd, tmpResultStringRef);
|
|
if (endStart < 0) {
|
|
return Ref<Result>();
|
|
}
|
|
|
|
int endGuardBegin;
|
|
int endGuardEnd;
|
|
if (!decodeEnd(row, endStart, &endGuardBegin, &endGuardEnd)) {
|
|
return Ref<Result>();
|
|
}
|
|
|
|
// Make sure there is a quiet zone at least as big as the end pattern after the barcode.
|
|
// The spec might want more whitespace, but in practice this is the maximum we can count on.
|
|
size_t quietEnd = endGuardEnd + (endGuardEnd - endGuardBegin);
|
|
if (quietEnd >= row->getSize() || !row->isRange(endGuardEnd, quietEnd, false)) {
|
|
return Ref<Result>();
|
|
}
|
|
|
|
if (!checkChecksum(tmpResultString)) {
|
|
return Ref<Result>();
|
|
}
|
|
|
|
Ref<String> resultString(new String(tmpResultString));
|
|
float left = (float) (startGuardBegin + startGuardEnd) / 2.0f;
|
|
float right = (float) (endGuardBegin + endGuardEnd) / 2.0f;
|
|
|
|
std::vector< Ref<ResultPoint> > resultPoints(2);
|
|
Ref<OneDResultPoint> resultPoint1(new OneDResultPoint(left, (float) rowNumber));
|
|
Ref<OneDResultPoint> resultPoint2(new OneDResultPoint(right, (float) rowNumber));
|
|
resultPoints[0] = resultPoint1;
|
|
resultPoints[1] = resultPoint2;
|
|
|
|
ArrayRef<unsigned char> resultBytes(1);
|
|
return Ref<Result>(new Result(resultString, resultBytes, resultPoints, getBarcodeFormat()));
|
|
}
|
|
|
|
bool UPCEANReader::findStartGuardPattern(Ref<BitArray> row, int* rangeStart, int* rangeEnd) {
|
|
int nextStart = 0;
|
|
while (findGuardPattern(row, nextStart, false, START_END_PATTERN,
|
|
sizeof(START_END_PATTERN) / sizeof(int), rangeStart, rangeEnd)) {
|
|
int start = *rangeStart;
|
|
nextStart = *rangeEnd;
|
|
// Make sure there is a quiet zone at least as big as the start pattern before the barcode.
|
|
// If this check would run off the left edge of the image, do not accept this barcode,
|
|
// as it is very likely to be a false positive.
|
|
int quietStart = start - (nextStart - start);
|
|
if (quietStart >= 0 && row->isRange(quietStart, start, false)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UPCEANReader::findGuardPattern(Ref<BitArray> row, int rowOffset, bool whiteFirst,
|
|
const int pattern[], int patternLen, int* start, int* end) {
|
|
int patternLength = patternLen;
|
|
int counters[patternLength];
|
|
int countersCount = sizeof(counters) / sizeof(int);
|
|
for (int i = 0; i < countersCount; i++) {
|
|
counters[i] = 0;
|
|
}
|
|
int width = row->getSize();
|
|
bool isWhite = false;
|
|
while (rowOffset < width) {
|
|
isWhite = !row->get(rowOffset);
|
|
if (whiteFirst == isWhite) {
|
|
break;
|
|
}
|
|
rowOffset++;
|
|
}
|
|
|
|
int counterPosition = 0;
|
|
int patternStart = rowOffset;
|
|
for (int x = rowOffset; x < width; x++) {
|
|
bool pixel = row->get(x);
|
|
if (pixel ^ isWhite) {
|
|
counters[counterPosition]++;
|
|
} else {
|
|
if (counterPosition == patternLength - 1) {
|
|
if (patternMatchVariance(counters, countersCount, pattern,
|
|
MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
|
|
*start = patternStart;
|
|
*end = x;
|
|
return true;
|
|
}
|
|
patternStart += counters[0] + counters[1];
|
|
for (int y = 2; y < patternLength; y++) {
|
|
counters[y - 2] = counters[y];
|
|
}
|
|
counters[patternLength - 2] = 0;
|
|
counters[patternLength - 1] = 0;
|
|
counterPosition--;
|
|
} else {
|
|
counterPosition++;
|
|
}
|
|
counters[counterPosition] = 1;
|
|
isWhite = !isWhite;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UPCEANReader::decodeEnd(Ref<BitArray> row, int endStart, int* endGuardBegin,
|
|
int* endGuardEnd) {
|
|
return findGuardPattern(row, endStart, false, START_END_PATTERN,
|
|
sizeof(START_END_PATTERN) / sizeof(int), endGuardBegin, endGuardEnd);
|
|
}
|
|
|
|
int UPCEANReader::decodeDigit(Ref<BitArray> row, int counters[], int countersLen, int rowOffset,
|
|
UPC_EAN_PATTERNS patternType) {
|
|
if (!recordPattern(row, rowOffset, counters, countersLen)) {
|
|
return -1;
|
|
}
|
|
unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
|
int bestMatch = -1;
|
|
|
|
int max = 0;
|
|
switch (patternType) {
|
|
case UPC_EAN_PATTERNS_L_PATTERNS:
|
|
max = L_PATTERNS_LEN;
|
|
for (int i = 0; i < max; i++) {
|
|
int pattern[countersLen];
|
|
for(int j = 0; j< countersLen; j++){
|
|
pattern[j] = L_PATTERNS[i][j];
|
|
}
|
|
|
|
unsigned int variance = patternMatchVariance(counters, countersLen, pattern,
|
|
MAX_INDIVIDUAL_VARIANCE);
|
|
if (variance < bestVariance) {
|
|
bestVariance = variance;
|
|
bestMatch = i;
|
|
}
|
|
}
|
|
break;
|
|
case UPC_EAN_PATTERNS_L_AND_G_PATTERNS:
|
|
max = L_AND_G_PATTERNS_LEN;
|
|
for (int i = 0; i < max; i++) {
|
|
int pattern[countersLen];
|
|
for(int j = 0; j< countersLen; j++){
|
|
pattern[j] = L_AND_G_PATTERNS[i][j];
|
|
}
|
|
|
|
unsigned int variance = patternMatchVariance(counters, countersLen, pattern,
|
|
MAX_INDIVIDUAL_VARIANCE);
|
|
if (variance < bestVariance) {
|
|
bestVariance = variance;
|
|
bestMatch = i;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return bestMatch;
|
|
}
|
|
|
|
/**
|
|
* @return {@link #checkStandardUPCEANChecksum(String)}
|
|
*/
|
|
bool UPCEANReader::checkChecksum(std::string s) {
|
|
return checkStandardUPCEANChecksum(s);
|
|
}
|
|
|
|
/**
|
|
* Computes the UPC/EAN checksum on a string of digits, and reports
|
|
* whether the checksum is correct or not.
|
|
*
|
|
* @param s string of digits to check
|
|
* @return true iff string of digits passes the UPC/EAN checksum algorithm
|
|
*/
|
|
bool UPCEANReader::checkStandardUPCEANChecksum(std::string s) {
|
|
int length = s.length();
|
|
if (length == 0) {
|
|
return false;
|
|
}
|
|
|
|
int sum = 0;
|
|
for (int i = length - 2; i >= 0; i -= 2) {
|
|
int digit = (int) s[i] - (int) '0';
|
|
if (digit < 0 || digit > 9) {
|
|
return false;
|
|
}
|
|
sum += digit;
|
|
}
|
|
sum *= 3;
|
|
for (int i = length - 1; i >= 0; i -= 2) {
|
|
int digit = (int) s[i] - (int) '0';
|
|
if (digit < 0 || digit > 9) {
|
|
return false;
|
|
}
|
|
sum += digit;
|
|
}
|
|
return sum % 10 == 0;
|
|
}
|
|
|
|
UPCEANReader::~UPCEANReader() {
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/oned/UPCEReader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Copyright 2010 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 "UPCEReader.h"
|
|
// #include <zxing/ReaderException.h>
|
|
|
|
namespace zxing {
|
|
namespace oned {
|
|
|
|
/**
|
|
* The pattern that marks the middle, and end, of a UPC-E pattern.
|
|
* There is no "second half" to a UPC-E barcode.
|
|
*/
|
|
static const int MIDDLE_END_PATTERN[6] = {1, 1, 1, 1, 1, 1};
|
|
|
|
/**
|
|
* See {@link #L_AND_G_PATTERNS}; these values similarly represent patterns of
|
|
* even-odd parity encodings of digits that imply both the number system (0 or 1)
|
|
* used, and the check digit.
|
|
*/
|
|
static const int NUMSYS_AND_CHECK_DIGIT_PATTERNS[2][10] = {
|
|
{0x38, 0x34, 0x32, 0x31, 0x2C, 0x26, 0x23, 0x2A, 0x29, 0x25},
|
|
{0x07, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A}
|
|
};
|
|
|
|
UPCEReader::UPCEReader() {
|
|
}
|
|
|
|
int UPCEReader::decodeMiddle(Ref<BitArray> row, int startGuardBegin, int startGuardEnd,
|
|
std::string& resultString) {
|
|
(void)startGuardBegin;
|
|
const int countersLen = 4;
|
|
int counters[countersLen] = { 0, 0, 0, 0 };
|
|
|
|
int end = row->getSize();
|
|
int rowOffset = startGuardEnd;
|
|
int lgPatternFound = 0;
|
|
|
|
for (int x = 0; x < 6 && rowOffset < end; x++) {
|
|
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset,
|
|
UPC_EAN_PATTERNS_L_AND_G_PATTERNS);
|
|
if (bestMatch < 0) {
|
|
return -1;
|
|
}
|
|
resultString.append(1, (char) ('0' + bestMatch % 10));
|
|
for (int i = 0; i < countersLen; i++) {
|
|
rowOffset += counters[i];
|
|
}
|
|
if (bestMatch >= 10) {
|
|
lgPatternFound |= 1 << (5 - x);
|
|
}
|
|
}
|
|
|
|
if (!determineNumSysAndCheckDigit(resultString, lgPatternFound)) {
|
|
return -1;
|
|
}
|
|
return rowOffset;
|
|
}
|
|
|
|
bool UPCEReader::decodeEnd(Ref<BitArray> row, int endStart, int* endGuardBegin,
|
|
int* endGuardEnd) {
|
|
return findGuardPattern(row, endStart, true, MIDDLE_END_PATTERN,
|
|
sizeof(MIDDLE_END_PATTERN) / sizeof(int), endGuardBegin, endGuardEnd);
|
|
}
|
|
|
|
bool UPCEReader::checkChecksum(std::string s){
|
|
return UPCEANReader::checkChecksum(convertUPCEtoUPCA(s));
|
|
}
|
|
|
|
|
|
bool UPCEReader::determineNumSysAndCheckDigit(std::string& resultString, int lgPatternFound) {
|
|
for (int numSys = 0; numSys <= 1; numSys++) {
|
|
for (int d = 0; d < 10; d++) {
|
|
if (lgPatternFound == NUMSYS_AND_CHECK_DIGIT_PATTERNS[numSys][d]) {
|
|
resultString.insert(0, 1, (char) ('0' + numSys));
|
|
resultString.append(1, (char) ('0' + d));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Expands a UPC-E value back into its full, equivalent UPC-A code value.
|
|
*
|
|
* @param upce UPC-E code as string of digits
|
|
* @return equivalent UPC-A code as string of digits
|
|
*/
|
|
std::string UPCEReader::convertUPCEtoUPCA(std::string upce) {
|
|
std::string result;
|
|
result.append(1, upce[0]);
|
|
char lastChar = upce[6];
|
|
switch (lastChar) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
result.append(upce.substr(1,2));
|
|
result.append(1, lastChar);
|
|
result.append("0000");
|
|
result.append(upce.substr(3,3));
|
|
break;
|
|
case '3':
|
|
result.append(upce.substr(1,3));
|
|
result.append("00000");
|
|
result.append(upce.substr(4,2));
|
|
break;
|
|
case '4':
|
|
result.append(upce.substr(1,4));
|
|
result.append("00000");
|
|
result.append(1, upce[5]);
|
|
break;
|
|
default:
|
|
result.append(upce.substr(1,5));
|
|
result.append("0000");
|
|
result.append(1, lastChar);
|
|
break;
|
|
}
|
|
result.append(1, upce[7]);
|
|
return result;
|
|
}
|
|
|
|
|
|
BarcodeFormat UPCEReader::getBarcodeFormat() {
|
|
return BarcodeFormat_UPC_E;
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/ErrorCorrectionLevel.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* ErrorCorrectionLevel.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 15/05/2008.
|
|
* Copyright 2008-2011 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/ErrorCorrectionLevel.h>
|
|
|
|
using std::string;
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
ErrorCorrectionLevel::ErrorCorrectionLevel(int inOrdinal,
|
|
int bits,
|
|
char const* name) :
|
|
ordinal_(inOrdinal), bits_(bits), name_(name) {}
|
|
|
|
int ErrorCorrectionLevel::ordinal() const {
|
|
return ordinal_;
|
|
}
|
|
|
|
int ErrorCorrectionLevel::bits() const {
|
|
return bits_;
|
|
}
|
|
|
|
string const& ErrorCorrectionLevel::name() const {
|
|
return name_;
|
|
}
|
|
|
|
ErrorCorrectionLevel::operator string const& () const {
|
|
return name_;
|
|
}
|
|
|
|
ErrorCorrectionLevel& ErrorCorrectionLevel::forBits(int bits) {
|
|
if (bits < 0 || bits >= N_LEVELS) {
|
|
throw ReaderException("Ellegal error correction level bits");
|
|
}
|
|
return *FOR_BITS[bits];
|
|
}
|
|
|
|
ErrorCorrectionLevel ErrorCorrectionLevel::L(0, 0x01, "L");
|
|
ErrorCorrectionLevel ErrorCorrectionLevel::M(1, 0x00, "M");
|
|
ErrorCorrectionLevel ErrorCorrectionLevel::Q(2, 0x03, "Q");
|
|
ErrorCorrectionLevel ErrorCorrectionLevel::H(3, 0x02, "H");
|
|
ErrorCorrectionLevel *ErrorCorrectionLevel::FOR_BITS[] = { &M, &L, &H, &Q };
|
|
int ErrorCorrectionLevel::N_LEVELS = 4;
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/FormatInformation.cpp
|
|
|
|
/*
|
|
* FormatInformation.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 18/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/FormatInformation.h>
|
|
// #include <limits>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
int FormatInformation::FORMAT_INFO_MASK_QR = 0x5412;
|
|
int FormatInformation::FORMAT_INFO_DECODE_LOOKUP[][2] = { { 0x5412, 0x00 }, { 0x5125, 0x01 }, { 0x5E7C, 0x02 }, {
|
|
0x5B4B, 0x03 }, { 0x45F9, 0x04 }, { 0x40CE, 0x05 }, { 0x4F97, 0x06 }, { 0x4AA0, 0x07 }, { 0x77C4, 0x08 }, {
|
|
0x72F3, 0x09 }, { 0x7DAA, 0x0A }, { 0x789D, 0x0B }, { 0x662F, 0x0C }, { 0x6318, 0x0D }, { 0x6C41, 0x0E }, {
|
|
0x6976, 0x0F }, { 0x1689, 0x10 }, { 0x13BE, 0x11 }, { 0x1CE7, 0x12 }, { 0x19D0, 0x13 }, { 0x0762, 0x14 }, {
|
|
0x0255, 0x15 }, { 0x0D0C, 0x16 }, { 0x083B, 0x17 }, { 0x355F, 0x18 }, { 0x3068, 0x19 }, { 0x3F31, 0x1A }, {
|
|
0x3A06, 0x1B }, { 0x24B4, 0x1C }, { 0x2183, 0x1D }, { 0x2EDA, 0x1E }, { 0x2BED, 0x1F },
|
|
};
|
|
int FormatInformation::N_FORMAT_INFO_DECODE_LOOKUPS = 32;
|
|
|
|
int FormatInformation::BITS_SET_IN_HALF_BYTE[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
|
|
|
|
FormatInformation::FormatInformation(int formatInfo) :
|
|
errorCorrectionLevel_(ErrorCorrectionLevel::forBits((formatInfo >> 3) & 0x03)), dataMask_(
|
|
(unsigned char)(formatInfo & 0x07)) {
|
|
}
|
|
|
|
ErrorCorrectionLevel& FormatInformation::getErrorCorrectionLevel() {
|
|
return errorCorrectionLevel_;
|
|
}
|
|
|
|
unsigned char FormatInformation::getDataMask() {
|
|
return dataMask_;
|
|
}
|
|
|
|
int FormatInformation::numBitsDiffering(unsigned int a, unsigned int b) {
|
|
a ^= b;
|
|
return BITS_SET_IN_HALF_BYTE[a & 0x0F] + BITS_SET_IN_HALF_BYTE[(a >> 4 & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 8
|
|
& 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 12 & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 16 & 0x0F)]
|
|
+ BITS_SET_IN_HALF_BYTE[(a >> 20 & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 24 & 0x0F)]
|
|
+ BITS_SET_IN_HALF_BYTE[(a >> 28 & 0x0F)];
|
|
}
|
|
|
|
Ref<FormatInformation> FormatInformation::decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
|
|
Ref<FormatInformation> result(doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2));
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
// Should return null, but, some QR codes apparently
|
|
// do not mask this info. Try again by actually masking the pattern
|
|
// first
|
|
return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
|
|
maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
|
|
}
|
|
Ref<FormatInformation> FormatInformation::doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
|
|
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
|
|
int bestDifference = numeric_limits<int>::max();
|
|
int bestFormatInfo = 0;
|
|
for (int i = 0; i < N_FORMAT_INFO_DECODE_LOOKUPS; i++) {
|
|
int* decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
|
|
int targetInfo = decodeInfo[0];
|
|
if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
|
|
// Found an exact match
|
|
Ref<FormatInformation> result(new FormatInformation(decodeInfo[1]));
|
|
return result;
|
|
}
|
|
int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
|
|
if (bitsDifference < bestDifference) {
|
|
bestFormatInfo = decodeInfo[1];
|
|
bestDifference = bitsDifference;
|
|
}
|
|
if (maskedFormatInfo1 != maskedFormatInfo2) {
|
|
// also try the other option
|
|
bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
|
|
if (bitsDifference < bestDifference) {
|
|
bestFormatInfo = decodeInfo[1];
|
|
bestDifference = bitsDifference;
|
|
}
|
|
}
|
|
}
|
|
if (bestDifference <= 3) {
|
|
Ref<FormatInformation> result(new FormatInformation(bestFormatInfo));
|
|
return result;
|
|
}
|
|
Ref<FormatInformation> result;
|
|
return result;
|
|
}
|
|
|
|
bool operator==(const FormatInformation &a, const FormatInformation &b) {
|
|
return &(a.errorCorrectionLevel_) == &(b.errorCorrectionLevel_) && a.dataMask_ == b.dataMask_;
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const FormatInformation& fi) {
|
|
const FormatInformation *fip = &fi;
|
|
out << "FormatInformation @ " << fip;
|
|
return out;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/QRCodeReader.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* QRCodeReader.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/QRCodeReader.h>
|
|
// #include <zxing/qrcode/detector/Detector.h>
|
|
|
|
// #include <iostream>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
QRCodeReader::QRCodeReader() :decoder_() {
|
|
}
|
|
//TODO: see if any of the other files in the qrcode tree need tryHarder
|
|
Ref<Result> QRCodeReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
|
#ifdef DEBUG
|
|
cout << "decoding image " << image.object_ << ":\n" << flush;
|
|
#endif
|
|
|
|
Detector detector(image->getBlackMatrix());
|
|
|
|
|
|
#ifdef DEBUG
|
|
cout << "(1) created detector " << &detector << "\n" << flush;
|
|
#endif
|
|
|
|
Ref<DetectorResult> detectorResult(detector.detect(hints));
|
|
#ifdef DEBUG
|
|
cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush;
|
|
#endif
|
|
|
|
std::vector<Ref<ResultPoint> > points(detectorResult->getPoints());
|
|
|
|
|
|
#ifdef DEBUG
|
|
cout << "(3) extracted points " << &points << "\n" << flush;
|
|
cout << "found " << points.size() << " points:\n";
|
|
for (size_t i = 0; i < points.size(); i++) {
|
|
cout << " " << points[i]->getX() << "," << points[i]->getY() << "\n";
|
|
}
|
|
cout << "bits:\n";
|
|
cout << *(detectorResult->getBits()) << "\n";
|
|
#endif
|
|
|
|
Ref<DecoderResult> decoderResult(decoder_.decode(detectorResult->getBits()));
|
|
#ifdef DEBUG
|
|
cout << "(4) decoded, have decoderResult " << decoderResult.object_ << "\n" << flush;
|
|
#endif
|
|
|
|
Ref<Result> result(
|
|
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_QR_CODE));
|
|
#ifdef DEBUG
|
|
cout << "(5) created result " << result.object_ << ", returning\n" << flush;
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
QRCodeReader::~QRCodeReader() {
|
|
}
|
|
|
|
Decoder& QRCodeReader::getDecoder() {
|
|
return decoder_;
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/Version.cpp
|
|
|
|
/*
|
|
* Version.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 14/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/Version.h>
|
|
// #include <zxing/qrcode/FormatInformation.h>
|
|
// #include <limits>
|
|
// #include <iostream>
|
|
// #include <cstdarg>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
using namespace std;
|
|
|
|
ECB::ECB(int count, int dataCodewords) :
|
|
count_(count), dataCodewords_(dataCodewords) {
|
|
}
|
|
|
|
int ECB::getCount() {
|
|
return count_;
|
|
}
|
|
|
|
int ECB::getDataCodewords() {
|
|
return dataCodewords_;
|
|
}
|
|
|
|
ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks) :
|
|
ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks) {
|
|
}
|
|
|
|
ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks1, ECB *ecBlocks2) :
|
|
ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks1) {
|
|
ecBlocks_.push_back(ecBlocks2);
|
|
}
|
|
|
|
int ECBlocks::getECCodewords() {
|
|
return ecCodewords_;
|
|
}
|
|
|
|
std::vector<ECB*>& ECBlocks::getECBlocks() {
|
|
return ecBlocks_;
|
|
}
|
|
|
|
ECBlocks::~ECBlocks() {
|
|
for (size_t i = 0; i < ecBlocks_.size(); i++) {
|
|
delete ecBlocks_[i];
|
|
}
|
|
}
|
|
|
|
unsigned int Version::VERSION_DECODE_INFO[] = { 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D,
|
|
0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
|
|
0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, 0x2542E, 0x26A64,
|
|
0x27541, 0x28C69
|
|
};
|
|
int Version::N_VERSION_DECODE_INFOS = 34;
|
|
vector<Ref<Version> > Version::VERSIONS;
|
|
static int N_VERSIONS = Version::buildVersions();
|
|
|
|
int Version::getVersionNumber() {
|
|
return versionNumber_;
|
|
}
|
|
|
|
vector<int> &Version::getAlignmentPatternCenters() {
|
|
return alignmentPatternCenters_;
|
|
}
|
|
|
|
int Version::getTotalCodewords() {
|
|
return totalCodewords_;
|
|
}
|
|
|
|
int Version::getDimensionForVersion() {
|
|
return 17 + 4 * versionNumber_;
|
|
}
|
|
|
|
ECBlocks& Version::getECBlocksForLevel(ErrorCorrectionLevel &ecLevel) {
|
|
return *ecBlocks_[ecLevel.ordinal()];
|
|
}
|
|
|
|
Version *Version::getProvisionalVersionForDimension(int dimension) {
|
|
if (dimension % 4 != 1) {
|
|
throw ReaderException("Dimension must be 1 mod 4");
|
|
}
|
|
return Version::getVersionForNumber((dimension - 17) >> 2);
|
|
}
|
|
|
|
Version *Version::getVersionForNumber(int versionNumber) {
|
|
if (versionNumber < 1 || versionNumber > N_VERSIONS) {
|
|
throw ReaderException("versionNumber must be between 1 and 40");
|
|
}
|
|
|
|
return VERSIONS[versionNumber - 1];
|
|
}
|
|
|
|
Version::Version(int versionNumber, vector<int> *alignmentPatternCenters, ECBlocks *ecBlocks1, ECBlocks *ecBlocks2,
|
|
ECBlocks *ecBlocks3, ECBlocks *ecBlocks4) :
|
|
versionNumber_(versionNumber), alignmentPatternCenters_(*alignmentPatternCenters), ecBlocks_(4), totalCodewords_(0) {
|
|
ecBlocks_[0] = ecBlocks1;
|
|
ecBlocks_[1] = ecBlocks2;
|
|
ecBlocks_[2] = ecBlocks3;
|
|
ecBlocks_[3] = ecBlocks4;
|
|
|
|
int total = 0;
|
|
int ecCodewords = ecBlocks1->getECCodewords();
|
|
vector<ECB*> &ecbArray = ecBlocks1->getECBlocks();
|
|
for (size_t i = 0; i < ecbArray.size(); i++) {
|
|
ECB *ecBlock = ecbArray[i];
|
|
total += ecBlock->getCount() * (ecBlock->getDataCodewords() + ecCodewords);
|
|
}
|
|
totalCodewords_ = total;
|
|
}
|
|
|
|
Version::~Version() {
|
|
delete &alignmentPatternCenters_;
|
|
for (size_t i = 0; i < ecBlocks_.size(); i++) {
|
|
delete ecBlocks_[i];
|
|
}
|
|
}
|
|
|
|
Version *Version::decodeVersionInformation(unsigned int versionBits) {
|
|
int bestDifference = numeric_limits<int>::max();
|
|
size_t bestVersion = 0;
|
|
for (int i = 0; i < N_VERSION_DECODE_INFOS; i++) {
|
|
unsigned targetVersion = VERSION_DECODE_INFO[i];
|
|
// Do the version info bits match exactly? done.
|
|
if (targetVersion == versionBits) {
|
|
return getVersionForNumber(i + 7);
|
|
}
|
|
// Otherwise see if this is the closest to a real version info bit
|
|
// string we have seen so far
|
|
int bitsDifference = FormatInformation::numBitsDiffering(versionBits, targetVersion);
|
|
if (bitsDifference < bestDifference) {
|
|
bestVersion = i + 7;
|
|
bestDifference = bitsDifference;
|
|
}
|
|
}
|
|
// We can tolerate up to 3 bits of error since no two version info codewords will
|
|
// differ in less than 4 bits.
|
|
if (bestDifference <= 3) {
|
|
return getVersionForNumber(bestVersion);
|
|
}
|
|
// If we didn't find a close enough match, fail
|
|
return 0;
|
|
}
|
|
|
|
Ref<BitMatrix> Version::buildFunctionPattern() {
|
|
int dimension = getDimensionForVersion();
|
|
Ref<BitMatrix> functionPattern(new BitMatrix(dimension));
|
|
|
|
|
|
// Top left finder pattern + separator + format
|
|
functionPattern->setRegion(0, 0, 9, 9);
|
|
// Top right finder pattern + separator + format
|
|
functionPattern->setRegion(dimension - 8, 0, 8, 9);
|
|
// Bottom left finder pattern + separator + format
|
|
functionPattern->setRegion(0, dimension - 8, 9, 8);
|
|
|
|
|
|
// Alignment patterns
|
|
size_t max = alignmentPatternCenters_.size();
|
|
for (size_t x = 0; x < max; x++) {
|
|
int i = alignmentPatternCenters_[x] - 2;
|
|
for (size_t y = 0; y < max; y++) {
|
|
if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) {
|
|
// No alignment patterns near the three finder patterns
|
|
continue;
|
|
}
|
|
functionPattern->setRegion(alignmentPatternCenters_[y] - 2, i, 5, 5);
|
|
}
|
|
}
|
|
|
|
// Vertical timing pattern
|
|
functionPattern->setRegion(6, 9, 1, dimension - 17);
|
|
// Horizontal timing pattern
|
|
functionPattern->setRegion(9, 6, dimension - 17, 1);
|
|
|
|
if (versionNumber_ > 6) {
|
|
// Version info, top right
|
|
functionPattern->setRegion(dimension - 11, 0, 3, 6);
|
|
// Version info, bottom left
|
|
functionPattern->setRegion(0, dimension - 11, 6, 3);
|
|
}
|
|
|
|
|
|
//#ifdef DEBUG
|
|
// cout << "version " << versionNumber_ << " built function pattern:\n";
|
|
// cout << *functionPattern;
|
|
//#endif
|
|
|
|
return functionPattern;
|
|
}
|
|
|
|
static vector<int> *intArray(size_t n...) {
|
|
va_list ap;
|
|
va_start(ap, n);
|
|
vector<int> *result = new vector<int>(n);
|
|
for (size_t i = 0; i < n; i++) {
|
|
(*result)[i] = va_arg(ap, int);
|
|
}
|
|
va_end(ap);
|
|
return result;
|
|
}
|
|
|
|
int Version::buildVersions() {
|
|
VERSIONS.push_back(Ref<Version>(new Version(1, intArray(0),
|
|
new ECBlocks(7, new ECB(1, 19)),
|
|
new ECBlocks(10, new ECB(1, 16)),
|
|
new ECBlocks(13, new ECB(1, 13)),
|
|
new ECBlocks(17, new ECB(1, 9)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(2, intArray(2, 6, 18),
|
|
new ECBlocks(10, new ECB(1, 34)),
|
|
new ECBlocks(16, new ECB(1, 28)),
|
|
new ECBlocks(22, new ECB(1, 22)),
|
|
new ECBlocks(28, new ECB(1, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(3, intArray(2, 6, 22),
|
|
new ECBlocks(15, new ECB(1, 55)),
|
|
new ECBlocks(26, new ECB(1, 44)),
|
|
new ECBlocks(18, new ECB(2, 17)),
|
|
new ECBlocks(22, new ECB(2, 13)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(4, intArray(2, 6, 26),
|
|
new ECBlocks(20, new ECB(1, 80)),
|
|
new ECBlocks(18, new ECB(2, 32)),
|
|
new ECBlocks(26, new ECB(2, 24)),
|
|
new ECBlocks(16, new ECB(4, 9)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(5, intArray(2, 6, 30),
|
|
new ECBlocks(26, new ECB(1, 108)),
|
|
new ECBlocks(24, new ECB(2, 43)),
|
|
new ECBlocks(18, new ECB(2, 15),
|
|
new ECB(2, 16)),
|
|
new ECBlocks(22, new ECB(2, 11),
|
|
new ECB(2, 12)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(6, intArray(2, 6, 34),
|
|
new ECBlocks(18, new ECB(2, 68)),
|
|
new ECBlocks(16, new ECB(4, 27)),
|
|
new ECBlocks(24, new ECB(4, 19)),
|
|
new ECBlocks(28, new ECB(4, 15)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(7, intArray(3, 6, 22, 38),
|
|
new ECBlocks(20, new ECB(2, 78)),
|
|
new ECBlocks(18, new ECB(4, 31)),
|
|
new ECBlocks(18, new ECB(2, 14),
|
|
new ECB(4, 15)),
|
|
new ECBlocks(26, new ECB(4, 13),
|
|
new ECB(1, 14)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(8, intArray(3, 6, 24, 42),
|
|
new ECBlocks(24, new ECB(2, 97)),
|
|
new ECBlocks(22, new ECB(2, 38),
|
|
new ECB(2, 39)),
|
|
new ECBlocks(22, new ECB(4, 18),
|
|
new ECB(2, 19)),
|
|
new ECBlocks(26, new ECB(4, 14),
|
|
new ECB(2, 15)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(9, intArray(3, 6, 26, 46),
|
|
new ECBlocks(30, new ECB(2, 116)),
|
|
new ECBlocks(22, new ECB(3, 36),
|
|
new ECB(2, 37)),
|
|
new ECBlocks(20, new ECB(4, 16),
|
|
new ECB(4, 17)),
|
|
new ECBlocks(24, new ECB(4, 12),
|
|
new ECB(4, 13)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(10, intArray(3, 6, 28, 50),
|
|
new ECBlocks(18, new ECB(2, 68),
|
|
new ECB(2, 69)),
|
|
new ECBlocks(26, new ECB(4, 43),
|
|
new ECB(1, 44)),
|
|
new ECBlocks(24, new ECB(6, 19),
|
|
new ECB(2, 20)),
|
|
new ECBlocks(28, new ECB(6, 15),
|
|
new ECB(2, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(11, intArray(3, 6, 30, 54),
|
|
new ECBlocks(20, new ECB(4, 81)),
|
|
new ECBlocks(30, new ECB(1, 50),
|
|
new ECB(4, 51)),
|
|
new ECBlocks(28, new ECB(4, 22),
|
|
new ECB(4, 23)),
|
|
new ECBlocks(24, new ECB(3, 12),
|
|
new ECB(8, 13)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(12, intArray(3, 6, 32, 58),
|
|
new ECBlocks(24, new ECB(2, 92),
|
|
new ECB(2, 93)),
|
|
new ECBlocks(22, new ECB(6, 36),
|
|
new ECB(2, 37)),
|
|
new ECBlocks(26, new ECB(4, 20),
|
|
new ECB(6, 21)),
|
|
new ECBlocks(28, new ECB(7, 14),
|
|
new ECB(4, 15)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(13, intArray(3, 6, 34, 62),
|
|
new ECBlocks(26, new ECB(4, 107)),
|
|
new ECBlocks(22, new ECB(8, 37),
|
|
new ECB(1, 38)),
|
|
new ECBlocks(24, new ECB(8, 20),
|
|
new ECB(4, 21)),
|
|
new ECBlocks(22, new ECB(12, 11),
|
|
new ECB(4, 12)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(14, intArray(4, 6, 26, 46, 66),
|
|
new ECBlocks(30, new ECB(3, 115),
|
|
new ECB(1, 116)),
|
|
new ECBlocks(24, new ECB(4, 40),
|
|
new ECB(5, 41)),
|
|
new ECBlocks(20, new ECB(11, 16),
|
|
new ECB(5, 17)),
|
|
new ECBlocks(24, new ECB(11, 12),
|
|
new ECB(5, 13)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(15, intArray(4, 6, 26, 48, 70),
|
|
new ECBlocks(22, new ECB(5, 87),
|
|
new ECB(1, 88)),
|
|
new ECBlocks(24, new ECB(5, 41),
|
|
new ECB(5, 42)),
|
|
new ECBlocks(30, new ECB(5, 24),
|
|
new ECB(7, 25)),
|
|
new ECBlocks(24, new ECB(11, 12),
|
|
new ECB(7, 13)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(16, intArray(4, 6, 26, 50, 74),
|
|
new ECBlocks(24, new ECB(5, 98),
|
|
new ECB(1, 99)),
|
|
new ECBlocks(28, new ECB(7, 45),
|
|
new ECB(3, 46)),
|
|
new ECBlocks(24, new ECB(15, 19),
|
|
new ECB(2, 20)),
|
|
new ECBlocks(30, new ECB(3, 15),
|
|
new ECB(13, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(17, intArray(4, 6, 30, 54, 78),
|
|
new ECBlocks(28, new ECB(1, 107),
|
|
new ECB(5, 108)),
|
|
new ECBlocks(28, new ECB(10, 46),
|
|
new ECB(1, 47)),
|
|
new ECBlocks(28, new ECB(1, 22),
|
|
new ECB(15, 23)),
|
|
new ECBlocks(28, new ECB(2, 14),
|
|
new ECB(17, 15)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(18, intArray(4, 6, 30, 56, 82),
|
|
new ECBlocks(30, new ECB(5, 120),
|
|
new ECB(1, 121)),
|
|
new ECBlocks(26, new ECB(9, 43),
|
|
new ECB(4, 44)),
|
|
new ECBlocks(28, new ECB(17, 22),
|
|
new ECB(1, 23)),
|
|
new ECBlocks(28, new ECB(2, 14),
|
|
new ECB(19, 15)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(19, intArray(4, 6, 30, 58, 86),
|
|
new ECBlocks(28, new ECB(3, 113),
|
|
new ECB(4, 114)),
|
|
new ECBlocks(26, new ECB(3, 44),
|
|
new ECB(11, 45)),
|
|
new ECBlocks(26, new ECB(17, 21),
|
|
new ECB(4, 22)),
|
|
new ECBlocks(26, new ECB(9, 13),
|
|
new ECB(16, 14)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(20, intArray(4, 6, 34, 62, 90),
|
|
new ECBlocks(28, new ECB(3, 107),
|
|
new ECB(5, 108)),
|
|
new ECBlocks(26, new ECB(3, 41),
|
|
new ECB(13, 42)),
|
|
new ECBlocks(30, new ECB(15, 24),
|
|
new ECB(5, 25)),
|
|
new ECBlocks(28, new ECB(15, 15),
|
|
new ECB(10, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(21, intArray(5, 6, 28, 50, 72, 94),
|
|
new ECBlocks(28, new ECB(4, 116),
|
|
new ECB(4, 117)),
|
|
new ECBlocks(26, new ECB(17, 42)),
|
|
new ECBlocks(28, new ECB(17, 22),
|
|
new ECB(6, 23)),
|
|
new ECBlocks(30, new ECB(19, 16),
|
|
new ECB(6, 17)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(22, intArray(5, 6, 26, 50, 74, 98),
|
|
new ECBlocks(28, new ECB(2, 111),
|
|
new ECB(7, 112)),
|
|
new ECBlocks(28, new ECB(17, 46)),
|
|
new ECBlocks(30, new ECB(7, 24),
|
|
new ECB(16, 25)),
|
|
new ECBlocks(24, new ECB(34, 13)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(23, intArray(5, 6, 30, 54, 78, 102),
|
|
new ECBlocks(30, new ECB(4, 121),
|
|
new ECB(5, 122)),
|
|
new ECBlocks(28, new ECB(4, 47),
|
|
new ECB(14, 48)),
|
|
new ECBlocks(30, new ECB(11, 24),
|
|
new ECB(14, 25)),
|
|
new ECBlocks(30, new ECB(16, 15),
|
|
new ECB(14, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(24, intArray(5, 6, 28, 54, 80, 106),
|
|
new ECBlocks(30, new ECB(6, 117),
|
|
new ECB(4, 118)),
|
|
new ECBlocks(28, new ECB(6, 45),
|
|
new ECB(14, 46)),
|
|
new ECBlocks(30, new ECB(11, 24),
|
|
new ECB(16, 25)),
|
|
new ECBlocks(30, new ECB(30, 16),
|
|
new ECB(2, 17)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(25, intArray(5, 6, 32, 58, 84, 110),
|
|
new ECBlocks(26, new ECB(8, 106),
|
|
new ECB(4, 107)),
|
|
new ECBlocks(28, new ECB(8, 47),
|
|
new ECB(13, 48)),
|
|
new ECBlocks(30, new ECB(7, 24),
|
|
new ECB(22, 25)),
|
|
new ECBlocks(30, new ECB(22, 15),
|
|
new ECB(13, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(26, intArray(5, 6, 30, 58, 86, 114),
|
|
new ECBlocks(28, new ECB(10, 114),
|
|
new ECB(2, 115)),
|
|
new ECBlocks(28, new ECB(19, 46),
|
|
new ECB(4, 47)),
|
|
new ECBlocks(28, new ECB(28, 22),
|
|
new ECB(6, 23)),
|
|
new ECBlocks(30, new ECB(33, 16),
|
|
new ECB(4, 17)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(27, intArray(5, 6, 34, 62, 90, 118),
|
|
new ECBlocks(30, new ECB(8, 122),
|
|
new ECB(4, 123)),
|
|
new ECBlocks(28, new ECB(22, 45),
|
|
new ECB(3, 46)),
|
|
new ECBlocks(30, new ECB(8, 23),
|
|
new ECB(26, 24)),
|
|
new ECBlocks(30, new ECB(12, 15),
|
|
new ECB(28, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(28, intArray(6, 6, 26, 50, 74, 98, 122),
|
|
new ECBlocks(30, new ECB(3, 117),
|
|
new ECB(10, 118)),
|
|
new ECBlocks(28, new ECB(3, 45),
|
|
new ECB(23, 46)),
|
|
new ECBlocks(30, new ECB(4, 24),
|
|
new ECB(31, 25)),
|
|
new ECBlocks(30, new ECB(11, 15),
|
|
new ECB(31, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(29, intArray(6, 6, 30, 54, 78, 102, 126),
|
|
new ECBlocks(30, new ECB(7, 116),
|
|
new ECB(7, 117)),
|
|
new ECBlocks(28, new ECB(21, 45),
|
|
new ECB(7, 46)),
|
|
new ECBlocks(30, new ECB(1, 23),
|
|
new ECB(37, 24)),
|
|
new ECBlocks(30, new ECB(19, 15),
|
|
new ECB(26, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(30, intArray(6, 6, 26, 52, 78, 104, 130),
|
|
new ECBlocks(30, new ECB(5, 115),
|
|
new ECB(10, 116)),
|
|
new ECBlocks(28, new ECB(19, 47),
|
|
new ECB(10, 48)),
|
|
new ECBlocks(30, new ECB(15, 24),
|
|
new ECB(25, 25)),
|
|
new ECBlocks(30, new ECB(23, 15),
|
|
new ECB(25, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(31, intArray(6, 6, 30, 56, 82, 108, 134),
|
|
new ECBlocks(30, new ECB(13, 115),
|
|
new ECB(3, 116)),
|
|
new ECBlocks(28, new ECB(2, 46),
|
|
new ECB(29, 47)),
|
|
new ECBlocks(30, new ECB(42, 24),
|
|
new ECB(1, 25)),
|
|
new ECBlocks(30, new ECB(23, 15),
|
|
new ECB(28, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(32, intArray(6, 6, 34, 60, 86, 112, 138),
|
|
new ECBlocks(30, new ECB(17, 115)),
|
|
new ECBlocks(28, new ECB(10, 46),
|
|
new ECB(23, 47)),
|
|
new ECBlocks(30, new ECB(10, 24),
|
|
new ECB(35, 25)),
|
|
new ECBlocks(30, new ECB(19, 15),
|
|
new ECB(35, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(33, intArray(6, 6, 30, 58, 86, 114, 142),
|
|
new ECBlocks(30, new ECB(17, 115),
|
|
new ECB(1, 116)),
|
|
new ECBlocks(28, new ECB(14, 46),
|
|
new ECB(21, 47)),
|
|
new ECBlocks(30, new ECB(29, 24),
|
|
new ECB(19, 25)),
|
|
new ECBlocks(30, new ECB(11, 15),
|
|
new ECB(46, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(34, intArray(6, 6, 34, 62, 90, 118, 146),
|
|
new ECBlocks(30, new ECB(13, 115),
|
|
new ECB(6, 116)),
|
|
new ECBlocks(28, new ECB(14, 46),
|
|
new ECB(23, 47)),
|
|
new ECBlocks(30, new ECB(44, 24),
|
|
new ECB(7, 25)),
|
|
new ECBlocks(30, new ECB(59, 16),
|
|
new ECB(1, 17)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(35, intArray(7, 6, 30, 54, 78,
|
|
102, 126, 150),
|
|
new ECBlocks(30, new ECB(12, 121),
|
|
new ECB(7, 122)),
|
|
new ECBlocks(28, new ECB(12, 47),
|
|
new ECB(26, 48)),
|
|
new ECBlocks(30, new ECB(39, 24),
|
|
new ECB(14, 25)),
|
|
new ECBlocks(30, new ECB(22, 15),
|
|
new ECB(41, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(36, intArray(7, 6, 24, 50, 76,
|
|
102, 128, 154),
|
|
new ECBlocks(30, new ECB(6, 121),
|
|
new ECB(14, 122)),
|
|
new ECBlocks(28, new ECB(6, 47),
|
|
new ECB(34, 48)),
|
|
new ECBlocks(30, new ECB(46, 24),
|
|
new ECB(10, 25)),
|
|
new ECBlocks(30, new ECB(2, 15),
|
|
new ECB(64, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(37, intArray(7, 6, 28, 54, 80,
|
|
106, 132, 158),
|
|
new ECBlocks(30, new ECB(17, 122),
|
|
new ECB(4, 123)),
|
|
new ECBlocks(28, new ECB(29, 46),
|
|
new ECB(14, 47)),
|
|
new ECBlocks(30, new ECB(49, 24),
|
|
new ECB(10, 25)),
|
|
new ECBlocks(30, new ECB(24, 15),
|
|
new ECB(46, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(38, intArray(7, 6, 32, 58, 84,
|
|
110, 136, 162),
|
|
new ECBlocks(30, new ECB(4, 122),
|
|
new ECB(18, 123)),
|
|
new ECBlocks(28, new ECB(13, 46),
|
|
new ECB(32, 47)),
|
|
new ECBlocks(30, new ECB(48, 24),
|
|
new ECB(14, 25)),
|
|
new ECBlocks(30, new ECB(42, 15),
|
|
new ECB(32, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(39, intArray(7, 6, 26, 54, 82,
|
|
110, 138, 166),
|
|
new ECBlocks(30, new ECB(20, 117),
|
|
new ECB(4, 118)),
|
|
new ECBlocks(28, new ECB(40, 47),
|
|
new ECB(7, 48)),
|
|
new ECBlocks(30, new ECB(43, 24),
|
|
new ECB(22, 25)),
|
|
new ECBlocks(30, new ECB(10, 15),
|
|
new ECB(67, 16)))));
|
|
VERSIONS.push_back(Ref<Version>(new Version(40, intArray(7, 6, 30, 58, 86,
|
|
114, 142, 170),
|
|
new ECBlocks(30, new ECB(19, 118),
|
|
new ECB(6, 119)),
|
|
new ECBlocks(28, new ECB(18, 47),
|
|
new ECB(31, 48)),
|
|
new ECBlocks(30, new ECB(34, 24),
|
|
new ECB(34, 25)),
|
|
new ECBlocks(30, new ECB(20, 15),
|
|
new ECB(61, 16)))));
|
|
return VERSIONS.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/decoder/BitMatrixParser.cpp
|
|
|
|
/*
|
|
* 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 formatInfoBits1 = 0;
|
|
for (int i = 0; i < 6; i++) {
|
|
formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
|
|
}
|
|
// .. and skip a bit in the timing pattern ...
|
|
formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
|
|
formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
|
|
formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
|
|
// .. and skip a bit in the timing pattern ...
|
|
for (int j = 5; j >= 0; j--) {
|
|
formatInfoBits1 = copyBit(8, j, formatInfoBits1);
|
|
}
|
|
|
|
// Read the top-right/bottom-left pattern
|
|
int dimension = bitMatrix_->getDimension();
|
|
int formatInfoBits2 = 0;
|
|
int jMin = dimension - 7;
|
|
for (int j = dimension - 1; j >= jMin; j--) {
|
|
formatInfoBits2 = copyBit(8, j, formatInfoBits2);
|
|
}
|
|
for (int i = dimension - 8; i < dimension; i++) {
|
|
formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
|
|
}
|
|
|
|
parsedFormatInfo_ = FormatInformation::decodeFormatInformation(formatInfoBits1,formatInfoBits2);
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/decoder/DataBlock.cpp
|
|
|
|
/*
|
|
* DataBlock.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 19/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/DataBlock.h>
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
DataBlock::DataBlock(int numDataCodewords, ArrayRef<unsigned char> codewords) :
|
|
numDataCodewords_(numDataCodewords), codewords_(codewords) {
|
|
}
|
|
|
|
int DataBlock::getNumDataCodewords() {
|
|
return numDataCodewords_;
|
|
}
|
|
|
|
ArrayRef<unsigned char> DataBlock::getCodewords() {
|
|
return codewords_;
|
|
}
|
|
|
|
|
|
std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<unsigned char> rawCodewords, Version *version,
|
|
ErrorCorrectionLevel &ecLevel) {
|
|
|
|
|
|
// Figure out the number and size of data blocks used by this version and
|
|
// error correction level
|
|
ECBlocks &ecBlocks = version->getECBlocksForLevel(ecLevel);
|
|
|
|
|
|
// First count the total number of data blocks
|
|
int totalBlocks = 0;
|
|
vector<ECB*> ecBlockArray = ecBlocks.getECBlocks();
|
|
for (size_t i = 0; i < ecBlockArray.size(); i++) {
|
|
totalBlocks += ecBlockArray[i]->getCount();
|
|
}
|
|
|
|
// Now establish DataBlocks of the appropriate size and number of data codewords
|
|
std::vector<Ref<DataBlock> > result(totalBlocks);
|
|
int numResultBlocks = 0;
|
|
for (size_t j = 0; j < ecBlockArray.size(); j++) {
|
|
ECB *ecBlock = ecBlockArray[j];
|
|
for (int i = 0; i < ecBlock->getCount(); i++) {
|
|
int numDataCodewords = ecBlock->getDataCodewords();
|
|
int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;
|
|
ArrayRef<unsigned char> buffer(numBlockCodewords);
|
|
Ref<DataBlock> blockRef(new DataBlock(numDataCodewords, buffer));
|
|
result[numResultBlocks++] = blockRef;
|
|
}
|
|
}
|
|
|
|
// All blocks have the same amount of data, except that the last n
|
|
// (where n may be 0) have 1 more byte. Figure out where these start.
|
|
int shorterBlocksTotalCodewords = result[0]->codewords_.size();
|
|
int longerBlocksStartAt = result.size() - 1;
|
|
while (longerBlocksStartAt >= 0) {
|
|
int numCodewords = result[longerBlocksStartAt]->codewords_.size();
|
|
if (numCodewords == shorterBlocksTotalCodewords) {
|
|
break;
|
|
}
|
|
if (numCodewords != shorterBlocksTotalCodewords + 1) {
|
|
throw IllegalArgumentException("Data block sizes differ by more than 1");
|
|
}
|
|
longerBlocksStartAt--;
|
|
}
|
|
longerBlocksStartAt++;
|
|
|
|
int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewords();
|
|
// The last elements of result may be 1 element longer;
|
|
// first fill out as many elements as all of them have
|
|
int rawCodewordsOffset = 0;
|
|
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
|
|
for (int j = 0; j < numResultBlocks; j++) {
|
|
result[j]->codewords_[i] = rawCodewords[rawCodewordsOffset++];
|
|
}
|
|
}
|
|
// Fill out the last data block in the longer ones
|
|
for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
|
|
result[j]->codewords_[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
|
|
}
|
|
// Now add in error correction blocks
|
|
int max = result[0]->codewords_.size();
|
|
for (int i = shorterBlocksNumDataCodewords; i < max; i++) {
|
|
for (int j = 0; j < numResultBlocks; j++) {
|
|
int iOffset = j < longerBlocksStartAt ? i : i + 1;
|
|
result[j]->codewords_[iOffset] = rawCodewords[rawCodewordsOffset++];
|
|
}
|
|
}
|
|
|
|
if ((size_t)rawCodewordsOffset != rawCodewords.size()) {
|
|
throw IllegalArgumentException("rawCodewordsOffset != rawCodewords.length");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/decoder/DataMask.cpp
|
|
|
|
/*
|
|
* DataMask.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 19/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/DataMask.h>
|
|
|
|
// #include <zxing/common/IllegalArgumentException.h>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
DataMask::DataMask() {
|
|
}
|
|
|
|
DataMask::~DataMask() {
|
|
}
|
|
|
|
vector<Ref<DataMask> > DataMask::DATA_MASKS;
|
|
static int N_DATA_MASKS = DataMask::buildDataMasks();
|
|
|
|
DataMask &DataMask::forReference(int reference) {
|
|
if (reference < 0 || reference > 7) {
|
|
throw IllegalArgumentException("reference must be between 0 and 7");
|
|
}
|
|
return *DATA_MASKS[reference];
|
|
}
|
|
|
|
void DataMask::unmaskBitMatrix(BitMatrix& bits, size_t dimension) {
|
|
for (size_t y = 0; y < dimension; y++) {
|
|
for (size_t x = 0; x < dimension; x++) {
|
|
// TODO: check why the coordinates have to be swapped
|
|
if (isMasked(y, x)) {
|
|
bits.flip(x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 000: mask bits for which (x + y) mod 2 == 0
|
|
*/
|
|
class DataMask000 : public DataMask {
|
|
public:
|
|
bool isMasked(size_t x, size_t y) {
|
|
// return ((x + y) & 0x01) == 0;
|
|
return ((x + y) % 2) == 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 001: mask bits for which x mod 2 == 0
|
|
*/
|
|
class DataMask001 : public DataMask {
|
|
public:
|
|
bool isMasked(size_t x, size_t) {
|
|
// return (x & 0x01) == 0;
|
|
return (x % 2) == 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 010: mask bits for which y mod 3 == 0
|
|
*/
|
|
class DataMask010 : public DataMask {
|
|
public:
|
|
bool isMasked(size_t, size_t y) {
|
|
return y % 3 == 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 011: mask bits for which (x + y) mod 3 == 0
|
|
*/
|
|
class DataMask011 : public DataMask {
|
|
public:
|
|
bool isMasked(size_t x, size_t y) {
|
|
return (x + y) % 3 == 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 100: mask bits for which (x/2 + y/3) mod 2 == 0
|
|
*/
|
|
class DataMask100 : public DataMask {
|
|
public:
|
|
bool isMasked(size_t x, size_t y) {
|
|
// return (((x >> 1) + (y / 3)) & 0x01) == 0;
|
|
return (((x >> 1) + (y / 3)) % 2) == 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 101: mask bits for which xy mod 2 + xy mod 3 == 0
|
|
*/
|
|
class DataMask101 : public DataMask {
|
|
public:
|
|
bool isMasked(size_t x, size_t y) {
|
|
size_t temp = x * y;
|
|
// return (temp & 0x01) + (temp % 3) == 0;
|
|
return (temp % 2) + (temp % 3) == 0;
|
|
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
|
|
*/
|
|
class DataMask110 : public DataMask {
|
|
public:
|
|
bool isMasked(size_t x, size_t y) {
|
|
size_t temp = x * y;
|
|
// return (((temp & 0x01) + (temp % 3)) & 0x01) == 0;
|
|
return (((temp % 2) + (temp % 3)) % 2) == 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
|
|
*/
|
|
class DataMask111 : public DataMask {
|
|
public:
|
|
bool isMasked(size_t x, size_t y) {
|
|
// return ((((x + y) & 0x01) + ((x * y) % 3)) & 0x01) == 0;
|
|
return ((((x + y) % 2) + ((x * y) % 3)) % 2) == 0;
|
|
}
|
|
};
|
|
|
|
int DataMask::buildDataMasks() {
|
|
DATA_MASKS.push_back(Ref<DataMask> (new DataMask000()));
|
|
DATA_MASKS.push_back(Ref<DataMask> (new DataMask001()));
|
|
DATA_MASKS.push_back(Ref<DataMask> (new DataMask010()));
|
|
DATA_MASKS.push_back(Ref<DataMask> (new DataMask011()));
|
|
DATA_MASKS.push_back(Ref<DataMask> (new DataMask100()));
|
|
DATA_MASKS.push_back(Ref<DataMask> (new DataMask101()));
|
|
DATA_MASKS.push_back(Ref<DataMask> (new DataMask110()));
|
|
DATA_MASKS.push_back(Ref<DataMask> (new DataMask111()));
|
|
return DATA_MASKS.size();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/decoder/DecodedBitStreamParser.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* DecodedBitStreamParser.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/DecodedBitStreamParser.h>
|
|
// #include <zxing/common/CharacterSetECI.h>
|
|
// #include <zxing/FormatException.h>
|
|
// #include <zxing/common/StringUtils.h>
|
|
// #include <iostream>
|
|
#ifndef NO_ICONV
|
|
// #include <iconv.h>
|
|
#endif
|
|
|
|
// Required for compatibility. TODO: test on Symbian
|
|
#ifdef ZXING_ICONV_CONST
|
|
#undef ICONV_CONST
|
|
#define ICONV_CONST const
|
|
#endif
|
|
|
|
#ifndef ICONV_CONST
|
|
#define ICONV_CONST /**/
|
|
#endif
|
|
|
|
using namespace std;
|
|
using namespace zxing;
|
|
using namespace zxing::qrcode;
|
|
using namespace zxing::common;
|
|
|
|
const char DecodedBitStreamParser::ALPHANUMERIC_CHARS[] =
|
|
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
|
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
|
'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'
|
|
};
|
|
|
|
namespace {int GB2312_SUBSET = 1;}
|
|
|
|
void DecodedBitStreamParser::append(std::string &result,
|
|
string const& in,
|
|
const char *src) {
|
|
append(result, (unsigned char const*)in.c_str(), in.length(), src);
|
|
}
|
|
|
|
void DecodedBitStreamParser::append(std::string &result,
|
|
const unsigned char *bufIn,
|
|
size_t nIn,
|
|
const char *src) {
|
|
#ifndef NO_ICONV
|
|
if (nIn == 0) {
|
|
return;
|
|
}
|
|
|
|
iconv_t cd = iconv_open(StringUtils::UTF8, src);
|
|
if (cd == (iconv_t)-1) {
|
|
result.append((const char *)bufIn, nIn);
|
|
return;
|
|
}
|
|
|
|
const int maxOut = 4 * nIn + 1;
|
|
unsigned char* bufOut = new unsigned char[maxOut];
|
|
|
|
ICONV_CONST char *fromPtr = (ICONV_CONST char *)bufIn;
|
|
size_t nFrom = nIn;
|
|
char *toPtr = (char *)bufOut;
|
|
size_t nTo = maxOut;
|
|
|
|
while (nFrom > 0) {
|
|
size_t oneway = iconv(cd, &fromPtr, &nFrom, &toPtr, &nTo);
|
|
if (oneway == (size_t)(-1)) {
|
|
iconv_close(cd);
|
|
delete[] bufOut;
|
|
throw ReaderException("error converting characters");
|
|
}
|
|
}
|
|
iconv_close(cd);
|
|
|
|
int nResult = maxOut - nTo;
|
|
bufOut[nResult] = '\0';
|
|
result.append((const char *)bufOut);
|
|
delete[] bufOut;
|
|
#else
|
|
result.append((const char *)bufIn, nIn);
|
|
#endif
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeHanziSegment(Ref<BitSource> bits_,
|
|
string& result,
|
|
int count) {
|
|
BitSource& bits (*bits_);
|
|
// Don't crash trying to read more bits than we have available.
|
|
if (count * 13 > bits.available()) {
|
|
throw FormatException();
|
|
}
|
|
|
|
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
|
// and decode as GB2312 afterwards
|
|
size_t nBytes = 2 * count;
|
|
unsigned char* buffer = new unsigned char[nBytes];
|
|
int offset = 0;
|
|
while (count > 0) {
|
|
// Each 13 bits encodes a 2-byte character
|
|
int twoBytes = bits.readBits(13);
|
|
int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
|
|
if (assembledTwoBytes < 0x003BF) {
|
|
// In the 0xA1A1 to 0xAAFE range
|
|
assembledTwoBytes += 0x0A1A1;
|
|
} else {
|
|
// In the 0xB0A1 to 0xFAFE range
|
|
assembledTwoBytes += 0x0A6A1;
|
|
}
|
|
buffer[offset] = (unsigned char) ((assembledTwoBytes >> 8) & 0xFF);
|
|
buffer[offset + 1] = (unsigned char) (assembledTwoBytes & 0xFF);
|
|
offset += 2;
|
|
count--;
|
|
}
|
|
|
|
try {
|
|
append(result, buffer, nBytes, StringUtils::GB2312);
|
|
} catch (ReaderException const& re) {
|
|
delete [] buffer;
|
|
throw FormatException();
|
|
}
|
|
|
|
delete [] buffer;
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeKanjiSegment(Ref<BitSource> bits, std::string &result, int count) {
|
|
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
|
// and decode as Shift_JIS afterwards
|
|
size_t nBytes = 2 * count;
|
|
unsigned char* buffer = new unsigned char[nBytes];
|
|
int offset = 0;
|
|
while (count > 0) {
|
|
// Each 13 bits encodes a 2-byte character
|
|
|
|
int twoBytes = bits->readBits(13);
|
|
int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
|
if (assembledTwoBytes < 0x01F00) {
|
|
// In the 0x8140 to 0x9FFC range
|
|
assembledTwoBytes += 0x08140;
|
|
} else {
|
|
// In the 0xE040 to 0xEBBF range
|
|
assembledTwoBytes += 0x0C140;
|
|
}
|
|
buffer[offset] = (unsigned char)(assembledTwoBytes >> 8);
|
|
buffer[offset + 1] = (unsigned char)assembledTwoBytes;
|
|
offset += 2;
|
|
count--;
|
|
}
|
|
|
|
append(result, buffer, nBytes, StringUtils::SHIFT_JIS);
|
|
delete[] buffer;
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeByteSegment(Ref<BitSource> bits_,
|
|
string& result,
|
|
int count,
|
|
CharacterSetECI* currentCharacterSetECI,
|
|
ArrayRef< ArrayRef<unsigned char> >& byteSegments,
|
|
Hashtable const& hints) {
|
|
int nBytes = count;
|
|
BitSource& bits (*bits_);
|
|
// Don't crash trying to read more bits than we have available.
|
|
if (count << 3 > bits.available()) {
|
|
throw FormatException();
|
|
}
|
|
|
|
ArrayRef<unsigned char> bytes_ (count);
|
|
unsigned char* readBytes = &(*bytes_)[0];
|
|
for (int i = 0; i < count; i++) {
|
|
readBytes[i] = (unsigned char) bits.readBits(8);
|
|
}
|
|
string encoding;
|
|
if (currentCharacterSetECI == 0) {
|
|
// The spec isn't clear on this mode; see
|
|
// section 6.4.5: t does not say which encoding to assuming
|
|
// upon decoding. I have seen ISO-8859-1 used as well as
|
|
// Shift_JIS -- without anything like an ECI designator to
|
|
// give a hint.
|
|
encoding = StringUtils::guessEncoding(readBytes, count, hints);
|
|
} else {
|
|
encoding = currentCharacterSetECI->getEncodingName();
|
|
}
|
|
try {
|
|
append(result, readBytes, nBytes, encoding.c_str());
|
|
} catch (ReaderException const& re) {
|
|
throw FormatException();
|
|
}
|
|
byteSegments->values().push_back(bytes_);
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeNumericSegment(Ref<BitSource> bits, std::string &result, int count) {
|
|
int nBytes = count;
|
|
unsigned char* bytes = new unsigned char[nBytes];
|
|
int i = 0;
|
|
// Read three digits at a time
|
|
while (count >= 3) {
|
|
// Each 10 bits encodes three digits
|
|
if (bits->available() < 10) {
|
|
throw ReaderException("format exception");
|
|
}
|
|
int threeDigitsBits = bits->readBits(10);
|
|
if (threeDigitsBits >= 1000) {
|
|
ostringstream s;
|
|
s << "Illegal value for 3-digit unit: " << threeDigitsBits;
|
|
delete[] bytes;
|
|
throw ReaderException(s.str().c_str());
|
|
}
|
|
bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits / 100];
|
|
bytes[i++] = ALPHANUMERIC_CHARS[(threeDigitsBits / 10) % 10];
|
|
bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits % 10];
|
|
count -= 3;
|
|
}
|
|
if (count == 2) {
|
|
if (bits->available() < 7) {
|
|
throw ReaderException("format exception");
|
|
}
|
|
// Two digits left over to read, encoded in 7 bits
|
|
int twoDigitsBits = bits->readBits(7);
|
|
if (twoDigitsBits >= 100) {
|
|
ostringstream s;
|
|
s << "Illegal value for 2-digit unit: " << twoDigitsBits;
|
|
delete[] bytes;
|
|
throw ReaderException(s.str().c_str());
|
|
}
|
|
bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits / 10];
|
|
bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits % 10];
|
|
} else if (count == 1) {
|
|
if (bits->available() < 4) {
|
|
throw ReaderException("format exception");
|
|
}
|
|
// One digit left over to read
|
|
int digitBits = bits->readBits(4);
|
|
if (digitBits >= 10) {
|
|
ostringstream s;
|
|
s << "Illegal value for digit unit: " << digitBits;
|
|
delete[] bytes;
|
|
throw ReaderException(s.str().c_str());
|
|
}
|
|
bytes[i++] = ALPHANUMERIC_CHARS[digitBits];
|
|
}
|
|
append(result, bytes, nBytes, StringUtils::ASCII);
|
|
delete[] bytes;
|
|
}
|
|
|
|
char DecodedBitStreamParser::toAlphaNumericChar(size_t value) {
|
|
if (value >= sizeof(DecodedBitStreamParser::ALPHANUMERIC_CHARS)) {
|
|
throw FormatException();
|
|
}
|
|
return ALPHANUMERIC_CHARS[value];
|
|
}
|
|
|
|
void DecodedBitStreamParser::decodeAlphanumericSegment(Ref<BitSource> bits_,
|
|
string& result,
|
|
int count,
|
|
bool fc1InEffect) {
|
|
BitSource& bits (*bits_);
|
|
ostringstream bytes;
|
|
// Read two characters at a time
|
|
while (count > 1) {
|
|
int nextTwoCharsBits = bits.readBits(11);
|
|
bytes << toAlphaNumericChar(nextTwoCharsBits / 45);
|
|
bytes << toAlphaNumericChar(nextTwoCharsBits % 45);
|
|
count -= 2;
|
|
}
|
|
if (count == 1) {
|
|
// special case: one character left
|
|
bytes << toAlphaNumericChar(bits.readBits(6));
|
|
}
|
|
// See section 6.4.8.1, 6.4.8.2
|
|
string s = bytes.str();
|
|
if (fc1InEffect) {
|
|
// We need to massage the result a bit if in an FNC1 mode:
|
|
ostringstream r;
|
|
for (size_t i = 0; i < s.length(); i++) {
|
|
if (s[i] != '%') {
|
|
r << s[i];
|
|
} else {
|
|
if (i < s.length() - 1 && s[i + 1] == '%') {
|
|
// %% is rendered as %
|
|
r << s[i++];
|
|
} else {
|
|
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
|
r << (char)0x1D;
|
|
}
|
|
}
|
|
}
|
|
s = r.str();
|
|
}
|
|
append(result, s, StringUtils::ASCII);
|
|
}
|
|
|
|
namespace {
|
|
int parseECIValue(BitSource bits) {
|
|
int firstByte = bits.readBits(8);
|
|
if ((firstByte & 0x80) == 0) {
|
|
// just one byte
|
|
return firstByte & 0x7F;
|
|
}
|
|
if ((firstByte & 0xC0) == 0x80) {
|
|
// two bytes
|
|
int secondByte = bits.readBits(8);
|
|
return ((firstByte & 0x3F) << 8) | secondByte;
|
|
}
|
|
if ((firstByte & 0xE0) == 0xC0) {
|
|
// three bytes
|
|
int secondThirdBytes = bits.readBits(16);
|
|
return ((firstByte & 0x1F) << 16) | secondThirdBytes;
|
|
}
|
|
throw IllegalArgumentException("Bad ECI bits starting with byte ");
|
|
}
|
|
}
|
|
|
|
Ref<DecoderResult>
|
|
DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes,
|
|
Version* version,
|
|
ErrorCorrectionLevel const& ecLevel,
|
|
Hashtable const& hints) {
|
|
Ref<BitSource> bits_ (new BitSource(bytes));
|
|
BitSource& bits (*bits_);
|
|
string result;
|
|
CharacterSetECI* currentCharacterSetECI = 0;
|
|
bool fc1InEffect = false;
|
|
ArrayRef< ArrayRef<unsigned char> > byteSegments (size_t(0));
|
|
Mode* mode = 0;
|
|
do {
|
|
// While still another segment to read...
|
|
if (bits.available() < 4) {
|
|
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
|
mode = &Mode::TERMINATOR;
|
|
} else {
|
|
try {
|
|
mode = &Mode::forBits(bits.readBits(4)); // mode is encoded by 4 bits
|
|
} catch (IllegalArgumentException const& iae) {
|
|
throw iae;
|
|
// throw FormatException.getFormatInstance();
|
|
}
|
|
}
|
|
if (mode != &Mode::TERMINATOR) {
|
|
if ((mode == &Mode::FNC1_FIRST_POSITION) || (mode == &Mode::FNC1_SECOND_POSITION)) {
|
|
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
|
fc1InEffect = true;
|
|
} else if (mode == &Mode::STRUCTURED_APPEND) {
|
|
// not really supported; all we do is ignore it
|
|
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
|
bits.readBits(16);
|
|
} else if (mode == &Mode::ECI) {
|
|
// Count doesn't apply to ECI
|
|
int value = parseECIValue(bits);
|
|
currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue(value);
|
|
if (currentCharacterSetECI == 0) {
|
|
throw FormatException();
|
|
}
|
|
} else {
|
|
// First handle Hanzi mode which does not start with character count
|
|
if (mode == &Mode::HANZI) {
|
|
//chinese mode contains a sub set indicator right after mode indicator
|
|
int subset = bits.readBits(4);
|
|
int countHanzi = bits.readBits(mode->getCharacterCountBits(version));
|
|
if (subset == GB2312_SUBSET) {
|
|
decodeHanziSegment(bits_, result, countHanzi);
|
|
}
|
|
} else {
|
|
// "Normal" QR code modes:
|
|
// How many characters will follow, encoded in this mode?
|
|
int count = bits.readBits(mode->getCharacterCountBits(version));
|
|
if (mode == &Mode::NUMERIC) {
|
|
decodeNumericSegment(bits_, result, count);
|
|
} else if (mode == &Mode::ALPHANUMERIC) {
|
|
decodeAlphanumericSegment(bits_, result, count, fc1InEffect);
|
|
} else if (mode == &Mode::BYTE) {
|
|
decodeByteSegment(bits_, result, count, currentCharacterSetECI, byteSegments, hints);
|
|
} else if (mode == &Mode::KANJI) {
|
|
decodeKanjiSegment(bits_, result, count);
|
|
} else {
|
|
throw FormatException();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while (mode != &Mode::TERMINATOR);
|
|
|
|
return Ref<DecoderResult>(new DecoderResult(bytes, Ref<String>(new String(result)), byteSegments, (string)ecLevel));
|
|
}
|
|
|
|
|
|
// file: zxing/qrcode/decoder/Decoder.cpp
|
|
|
|
/*
|
|
* Decoder.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/Decoder.h>
|
|
// #include <zxing/qrcode/decoder/BitMatrixParser.h>
|
|
// #include <zxing/qrcode/ErrorCorrectionLevel.h>
|
|
// #include <zxing/qrcode/Version.h>
|
|
// #include <zxing/qrcode/decoder/DataBlock.h>
|
|
// #include <zxing/qrcode/decoder/DecodedBitStreamParser.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <zxing/common/reedsolomon/ReedSolomonException.h>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
Decoder::Decoder() :
|
|
rsDecoder_(GF256::QR_CODE_FIELD) {
|
|
}
|
|
|
|
void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCodewords) {
|
|
int numCodewords = codewordBytes->size();
|
|
ArrayRef<int> codewordInts(numCodewords);
|
|
for (int i = 0; i < numCodewords; i++) {
|
|
codewordInts[i] = codewordBytes[i] & 0xff;
|
|
}
|
|
int numECCodewords = numCodewords - numDataCodewords;
|
|
|
|
try {
|
|
rsDecoder_.decode(codewordInts, numECCodewords);
|
|
} catch (ReedSolomonException const& ex) {
|
|
ReaderException rex(ex.what());
|
|
throw rex;
|
|
}
|
|
|
|
for (int i = 0; i < numDataCodewords; i++) {
|
|
codewordBytes[i] = (unsigned char)codewordInts[i];
|
|
}
|
|
}
|
|
|
|
Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
|
|
// Construct a parser and read version, error-correction level
|
|
BitMatrixParser parser(bits);
|
|
|
|
Version *version = parser.readVersion();
|
|
ErrorCorrectionLevel &ecLevel = parser.readFormatInformation()->getErrorCorrectionLevel();
|
|
|
|
|
|
// Read codewords
|
|
ArrayRef<unsigned char> codewords(parser.readCodewords());
|
|
|
|
|
|
// Separate into data blocks
|
|
std::vector<Ref<DataBlock> > dataBlocks(DataBlock::getDataBlocks(codewords, version, ecLevel));
|
|
|
|
|
|
// Count total number of data bytes
|
|
int totalBytes = 0;
|
|
for (size_t i = 0; i < dataBlocks.size(); i++) {
|
|
totalBytes += dataBlocks[i]->getNumDataCodewords();
|
|
}
|
|
ArrayRef<unsigned char> resultBytes(totalBytes);
|
|
int resultOffset = 0;
|
|
|
|
|
|
// Error-correct and copy data blocks together into a stream of bytes
|
|
for (size_t j = 0; j < dataBlocks.size(); j++) {
|
|
Ref<DataBlock> dataBlock(dataBlocks[j]);
|
|
ArrayRef<unsigned char> codewordBytes = dataBlock->getCodewords();
|
|
int numDataCodewords = dataBlock->getNumDataCodewords();
|
|
correctErrors(codewordBytes, numDataCodewords);
|
|
for (int i = 0; i < numDataCodewords; i++) {
|
|
resultBytes[resultOffset++] = codewordBytes[i];
|
|
}
|
|
}
|
|
|
|
return DecodedBitStreamParser::decode(resultBytes,
|
|
version,
|
|
ecLevel,
|
|
DecodedBitStreamParser::Hashtable());
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/decoder/Mode.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Mode.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 19/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/Mode.h>
|
|
// #include <zxing/common/Counted.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <zxing/qrcode/Version.h>
|
|
// #include <sstream>
|
|
|
|
using zxing::qrcode::Mode;
|
|
using std::ostringstream;
|
|
|
|
Mode Mode::TERMINATOR(0, 0, 0, 0x00, "TERMINATOR");
|
|
Mode Mode::NUMERIC(10, 12, 14, 0x01, "NUMERIC");
|
|
Mode Mode::ALPHANUMERIC(9, 11, 13, 0x02, "ALPHANUMERIC");
|
|
Mode Mode::STRUCTURED_APPEND(0, 0, 0, 0x03, "STRUCTURED_APPEND");
|
|
Mode Mode::BYTE(8, 16, 16, 0x04, "BYTE");
|
|
Mode Mode::ECI(0, 0, 0, 0x07, "ECI");
|
|
Mode Mode::KANJI(8, 10, 12, 0x08, "KANJI");
|
|
Mode Mode::FNC1_FIRST_POSITION(0, 0, 0, 0x05, "FNC1_FIRST_POSITION");
|
|
Mode Mode::FNC1_SECOND_POSITION(0, 0, 0, 0x09, "FNC1_SECOND_POSITION");
|
|
Mode Mode::HANZI(8, 10, 12, 0x0D, "HANZI");
|
|
|
|
Mode::Mode(int cbv0_9, int cbv10_26, int cbv27, int bits, char const* name) :
|
|
characterCountBitsForVersions0To9_(cbv0_9), characterCountBitsForVersions10To26_(cbv10_26),
|
|
characterCountBitsForVersions27AndHigher_(cbv27), bits_(bits), name_(name) {
|
|
}
|
|
|
|
Mode& Mode::forBits(int bits) {
|
|
switch (bits) {
|
|
case 0x0:
|
|
return TERMINATOR;
|
|
case 0x1:
|
|
return NUMERIC;
|
|
case 0x2:
|
|
return ALPHANUMERIC;
|
|
case 0x3:
|
|
return STRUCTURED_APPEND;
|
|
case 0x4:
|
|
return BYTE;
|
|
case 0x5:
|
|
return FNC1_FIRST_POSITION;
|
|
case 0x7:
|
|
return ECI;
|
|
case 0x8:
|
|
return KANJI;
|
|
case 0x9:
|
|
return FNC1_SECOND_POSITION;
|
|
case 0xD:
|
|
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
|
|
return HANZI;
|
|
default:
|
|
ostringstream s;
|
|
s << "Illegal mode bits: " << bits;
|
|
throw ReaderException(s.str().c_str());
|
|
}
|
|
}
|
|
|
|
int Mode::getCharacterCountBits(Version *version) {
|
|
int number = version->getVersionNumber();
|
|
if (number <= 9) {
|
|
return characterCountBitsForVersions0To9_;
|
|
} else if (number <= 26) {
|
|
return characterCountBitsForVersions10To26_;
|
|
} else {
|
|
return characterCountBitsForVersions27AndHigher_;
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/detector/AlignmentPattern.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* AlignmentPattern.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 13/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/detector/AlignmentPattern.h>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
AlignmentPattern::AlignmentPattern(float posX, float posY, float estimatedModuleSize) :
|
|
ResultPoint(posX,posY), estimatedModuleSize_(estimatedModuleSize) {
|
|
}
|
|
|
|
bool AlignmentPattern::aboutEquals(float moduleSize, float i, float j) const {
|
|
if (abs(i - getY()) <= moduleSize && abs(j - getX()) <= moduleSize) {
|
|
float moduleSizeDiff = abs(moduleSize - estimatedModuleSize_);
|
|
return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize_;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Ref<AlignmentPattern> AlignmentPattern::combineEstimate(float i, float j, float newModuleSize) const {
|
|
float combinedX = (getX() + j) / 2.0f;
|
|
float combinedY = (getY() + i) / 2.0f;
|
|
float combinedModuleSize = (estimatedModuleSize_ + newModuleSize) / 2.0f;
|
|
Ref<AlignmentPattern> result
|
|
(new AlignmentPattern(combinedX, combinedY, combinedModuleSize));
|
|
return result;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/detector/AlignmentPatternFinder.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* AlignmentPatternFinder.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 14/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 "AlignmentPatternFinder.h"
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <zxing/common/BitArray.h>
|
|
// #include <vector>
|
|
// #include <cmath>
|
|
// #include <cstdlib>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
float AlignmentPatternFinder::centerFromEnd(vector<int> &stateCount, int end) {
|
|
return (float)(end - stateCount[2]) - stateCount[1] / 2.0f;
|
|
}
|
|
|
|
bool AlignmentPatternFinder::foundPatternCross(vector<int> &stateCount) {
|
|
float maxVariance = moduleSize_ / 2.0f;
|
|
for (size_t i = 0; i < 3; i++) {
|
|
if (abs(moduleSize_ - stateCount[i]) >= maxVariance) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
float AlignmentPatternFinder::crossCheckVertical(size_t startI, size_t centerJ, int maxCount,
|
|
int originalStateCountTotal) {
|
|
int maxI = image_->getHeight();
|
|
vector<int> stateCount(3, 0);
|
|
|
|
|
|
// Start counting up from center
|
|
int i = startI;
|
|
while (i >= 0 && image_->get(centerJ, i) && stateCount[1] <= maxCount) {
|
|
stateCount[1]++;
|
|
i--;
|
|
}
|
|
// If already too many modules in this state or ran off the edge:
|
|
if (i < 0 || stateCount[1] > maxCount) {
|
|
return NAN;
|
|
}
|
|
while (i >= 0 && !image_->get(centerJ, i) && stateCount[0] <= maxCount) {
|
|
stateCount[0]++;
|
|
i--;
|
|
}
|
|
if (stateCount[0] > maxCount) {
|
|
return NAN;
|
|
}
|
|
|
|
// Now also count down from center
|
|
i = startI + 1;
|
|
while (i < maxI && image_->get(centerJ, i) && stateCount[1] <= maxCount) {
|
|
stateCount[1]++;
|
|
i++;
|
|
}
|
|
if (i == maxI || stateCount[1] > maxCount) {
|
|
return NAN;
|
|
}
|
|
while (i < maxI && !image_->get(centerJ, i) && stateCount[2] <= maxCount) {
|
|
stateCount[2]++;
|
|
i++;
|
|
}
|
|
if (stateCount[2] > maxCount) {
|
|
return NAN;
|
|
}
|
|
|
|
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
|
|
if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
|
|
return NAN;
|
|
}
|
|
|
|
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : NAN;
|
|
}
|
|
|
|
Ref<AlignmentPattern> AlignmentPatternFinder::handlePossibleCenter(vector<int> &stateCount, size_t i, size_t j) {
|
|
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
|
|
float centerJ = centerFromEnd(stateCount, j);
|
|
float centerI = crossCheckVertical(i, (int)centerJ, 2 * stateCount[1], stateCountTotal);
|
|
if (!isnan(centerI)) {
|
|
float estimatedModuleSize = (float)(stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;
|
|
int max = possibleCenters_->size();
|
|
for (int index = 0; index < max; index++) {
|
|
Ref<AlignmentPattern> center((*possibleCenters_)[index]);
|
|
// Look for about the same center and module size:
|
|
if (center->aboutEquals(estimatedModuleSize, centerI, centerJ)) {
|
|
return center->combineEstimate(centerI, centerJ, estimatedModuleSize);
|
|
}
|
|
}
|
|
AlignmentPattern *tmp = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
|
|
// Hadn't found this before; save it
|
|
tmp->retain();
|
|
possibleCenters_->push_back(tmp);
|
|
if (callback_ != 0) {
|
|
callback_->foundPossibleResultPoint(*tmp);
|
|
}
|
|
}
|
|
Ref<AlignmentPattern> result;
|
|
return result;
|
|
}
|
|
|
|
AlignmentPatternFinder::AlignmentPatternFinder(Ref<BitMatrix> image, size_t startX, size_t startY, size_t width,
|
|
size_t height, float moduleSize,
|
|
Ref<ResultPointCallback>const& callback) :
|
|
image_(image), possibleCenters_(new vector<AlignmentPattern *> ()), startX_(startX), startY_(startY),
|
|
width_(width), height_(height), moduleSize_(moduleSize), callback_(callback) {
|
|
}
|
|
|
|
AlignmentPatternFinder::~AlignmentPatternFinder() {
|
|
for (size_t i = 0; i < possibleCenters_->size(); i++) {
|
|
(*possibleCenters_)[i]->release();
|
|
(*possibleCenters_)[i] = 0;
|
|
}
|
|
delete possibleCenters_;
|
|
}
|
|
|
|
Ref<AlignmentPattern> AlignmentPatternFinder::find() {
|
|
size_t maxJ = startX_ + width_;
|
|
size_t middleI = startY_ + (height_ >> 1);
|
|
// Ref<BitArray> luminanceRow(new BitArray(width_));
|
|
// We are looking for black/white/black modules in 1:1:1 ratio;
|
|
// this tracks the number of black/white/black modules seen so far
|
|
vector<int> stateCount(3, 0);
|
|
for (size_t iGen = 0; iGen < height_; iGen++) {
|
|
// Search from middle outwards
|
|
size_t i = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1));
|
|
// image_->getBlackRow(i, luminanceRow, startX_, width_);
|
|
stateCount[0] = 0;
|
|
stateCount[1] = 0;
|
|
stateCount[2] = 0;
|
|
size_t j = startX_;
|
|
// Burn off leading white pixels before anything else; if we start in the middle of
|
|
// a white run, it doesn't make sense to count its length, since we don't know if the
|
|
// white run continued to the left of the start point
|
|
while (j < maxJ && !image_->get(j, i)) {
|
|
j++;
|
|
}
|
|
int currentState = 0;
|
|
while (j < maxJ) {
|
|
if (image_->get(j, i)) {
|
|
// Black pixel
|
|
if (currentState == 1) { // Counting black pixels
|
|
stateCount[currentState]++;
|
|
} else { // Counting white pixels
|
|
if (currentState == 2) { // A winner?
|
|
if (foundPatternCross(stateCount)) { // Yes
|
|
Ref<AlignmentPattern> confirmed(handlePossibleCenter(stateCount, i, j));
|
|
if (confirmed != 0) {
|
|
return confirmed;
|
|
}
|
|
}
|
|
stateCount[0] = stateCount[2];
|
|
stateCount[1] = 1;
|
|
stateCount[2] = 0;
|
|
currentState = 1;
|
|
} else {
|
|
stateCount[++currentState]++;
|
|
}
|
|
}
|
|
} else { // White pixel
|
|
if (currentState == 1) { // Counting black pixels
|
|
currentState++;
|
|
}
|
|
stateCount[currentState]++;
|
|
}
|
|
j++;
|
|
}
|
|
if (foundPatternCross(stateCount)) {
|
|
Ref<AlignmentPattern> confirmed(handlePossibleCenter(stateCount, i, maxJ));
|
|
if (confirmed != 0) {
|
|
return confirmed;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Hmm, nothing we saw was observed and confirmed twice. If we had
|
|
// any guess at all, return it.
|
|
if (possibleCenters_->size() > 0) {
|
|
Ref<AlignmentPattern> center((*possibleCenters_)[0]);
|
|
return center;
|
|
}
|
|
|
|
throw zxing::ReaderException("Could not find alignment pattern");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/detector/Detector.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Detector.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 14/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/detector/Detector.h>
|
|
// #include <zxing/qrcode/detector/FinderPatternFinder.h>
|
|
// #include <zxing/qrcode/detector/FinderPattern.h>
|
|
// #include <zxing/qrcode/detector/AlignmentPattern.h>
|
|
// #include <zxing/qrcode/detector/AlignmentPatternFinder.h>
|
|
// #include <zxing/qrcode/Version.h>
|
|
// #include <zxing/common/GridSampler.h>
|
|
// #include <zxing/DecodeHints.h>
|
|
// #include <cmath>
|
|
// #include <sstream>
|
|
// #include <cstdlib>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
Detector::Detector(Ref<BitMatrix> image) :
|
|
image_(image) {
|
|
}
|
|
|
|
Ref<BitMatrix> Detector::getImage() {
|
|
return image_;
|
|
}
|
|
|
|
Ref<DetectorResult> Detector::detect(DecodeHints const& hints) {
|
|
callback_ = hints.getResultPointCallback();
|
|
FinderPatternFinder finder(image_, hints.getResultPointCallback());
|
|
Ref<FinderPatternInfo> info(finder.find(hints));
|
|
return processFinderPatternInfo(info);
|
|
}
|
|
|
|
Ref<DetectorResult> Detector::processFinderPatternInfo(Ref<FinderPatternInfo> info){
|
|
Ref<FinderPattern> topLeft(info->getTopLeft());
|
|
Ref<FinderPattern> topRight(info->getTopRight());
|
|
Ref<FinderPattern> bottomLeft(info->getBottomLeft());
|
|
|
|
float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);
|
|
if (moduleSize < 1.0f) {
|
|
throw zxing::ReaderException("bad module size");
|
|
}
|
|
int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize);
|
|
Version *provisionalVersion = Version::getProvisionalVersionForDimension(dimension);
|
|
int modulesBetweenFPCenters = provisionalVersion->getDimensionForVersion() - 7;
|
|
|
|
Ref<AlignmentPattern> alignmentPattern;
|
|
// Anything above version 1 has an alignment pattern
|
|
if (provisionalVersion->getAlignmentPatternCenters().size() > 0) {
|
|
|
|
|
|
// Guess where a "bottom right" finder pattern would have been
|
|
float bottomRightX = topRight->getX() - topLeft->getX() + bottomLeft->getX();
|
|
float bottomRightY = topRight->getY() - topLeft->getY() + bottomLeft->getY();
|
|
|
|
|
|
// Estimate that alignment pattern is closer by 3 modules
|
|
// from "bottom right" to known top left location
|
|
float correctionToTopLeft = 1.0f - 3.0f / (float)modulesBetweenFPCenters;
|
|
int estAlignmentX = (int)(topLeft->getX() + correctionToTopLeft * (bottomRightX - topLeft->getX()));
|
|
int estAlignmentY = (int)(topLeft->getY() + correctionToTopLeft * (bottomRightY - topLeft->getY()));
|
|
|
|
|
|
// Kind of arbitrary -- expand search radius before giving up
|
|
for (int i = 4; i <= 16; i <<= 1) {
|
|
try {
|
|
alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i);
|
|
break;
|
|
} catch (zxing::ReaderException const& re) {
|
|
// try next round
|
|
}
|
|
}
|
|
if (alignmentPattern == 0) {
|
|
// Try anyway
|
|
}
|
|
|
|
}
|
|
|
|
Ref<PerspectiveTransform> transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
|
|
Ref<BitMatrix> bits(sampleGrid(image_, dimension, transform));
|
|
std::vector<Ref<ResultPoint> > points(alignmentPattern == 0 ? 3 : 4);
|
|
points[0].reset(bottomLeft);
|
|
points[1].reset(topLeft);
|
|
points[2].reset(topRight);
|
|
if (alignmentPattern != 0) {
|
|
points[3].reset(alignmentPattern);
|
|
}
|
|
|
|
Ref<DetectorResult> result(new DetectorResult(bits, points, transform));
|
|
return result;
|
|
}
|
|
|
|
Ref<PerspectiveTransform> Detector::createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
|
|
ResultPoint > bottomLeft, Ref<ResultPoint> alignmentPattern, int dimension) {
|
|
|
|
float dimMinusThree = (float)dimension - 3.5f;
|
|
float bottomRightX;
|
|
float bottomRightY;
|
|
float sourceBottomRightX;
|
|
float sourceBottomRightY;
|
|
if (alignmentPattern != 0) {
|
|
bottomRightX = alignmentPattern->getX();
|
|
bottomRightY = alignmentPattern->getY();
|
|
sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f;
|
|
} else {
|
|
// Don't have an alignment pattern, just make up the bottom-right point
|
|
bottomRightX = (topRight->getX() - topLeft->getX()) + bottomLeft->getX();
|
|
bottomRightY = (topRight->getY() - topLeft->getY()) + bottomLeft->getY();
|
|
sourceBottomRightX = sourceBottomRightY = dimMinusThree;
|
|
}
|
|
|
|
Ref<PerspectiveTransform> transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, sourceBottomRightX,
|
|
sourceBottomRightY, 3.5f, dimMinusThree, topLeft->getX(), topLeft->getY(), topRight->getX(),
|
|
topRight->getY(), bottomRightX, bottomRightY, bottomLeft->getX(), bottomLeft->getY()));
|
|
|
|
return transform;
|
|
}
|
|
|
|
Ref<BitMatrix> Detector::sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform> transform) {
|
|
GridSampler &sampler = GridSampler::getInstance();
|
|
return sampler.sampleGrid(image, dimension, transform);
|
|
}
|
|
|
|
int Detector::computeDimension(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref<ResultPoint> bottomLeft,
|
|
float moduleSize) {
|
|
int tltrCentersDimension = int(FinderPatternFinder::distance(topLeft, topRight) / moduleSize + 0.5f);
|
|
int tlblCentersDimension = int(FinderPatternFinder::distance(topLeft, bottomLeft) / moduleSize + 0.5f);
|
|
int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
|
|
switch (dimension & 0x03) { // mod 4
|
|
case 0:
|
|
dimension++;
|
|
break;
|
|
// 1? do nothing
|
|
case 2:
|
|
dimension--;
|
|
break;
|
|
case 3:
|
|
ostringstream s;
|
|
s << "Bad dimension: " << dimension;
|
|
throw zxing::ReaderException(s.str().c_str());
|
|
}
|
|
return dimension;
|
|
}
|
|
|
|
float Detector::calculateModuleSize(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref<ResultPoint> bottomLeft) {
|
|
// Take the average
|
|
return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f;
|
|
}
|
|
|
|
float Detector::calculateModuleSizeOneWay(Ref<ResultPoint> pattern, Ref<ResultPoint> otherPattern) {
|
|
float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int)pattern->getX(), (int)pattern->getY(),
|
|
(int)otherPattern->getX(), (int)otherPattern->getY());
|
|
float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int)otherPattern->getX(), (int)otherPattern->getY(),
|
|
(int)pattern->getX(), (int)pattern->getY());
|
|
if (isnan(moduleSizeEst1)) {
|
|
return moduleSizeEst2;
|
|
}
|
|
if (isnan(moduleSizeEst2)) {
|
|
return moduleSizeEst1;
|
|
}
|
|
// Average them, and divide by 7 since we've counted the width of 3 black modules,
|
|
// and 1 white and 1 black module on either side. Ergo, divide sum by 14.
|
|
return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;
|
|
}
|
|
|
|
float Detector::sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) {
|
|
|
|
float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
|
|
|
|
// Now count other way -- don't run off image though of course
|
|
float scale = 1.0f;
|
|
int otherToX = fromX - (toX - fromX);
|
|
if (otherToX < 0) {
|
|
scale = (float) fromX / (float) (fromX - otherToX);
|
|
otherToX = 0;
|
|
} else if (otherToX >= (int)image_->getWidth()) {
|
|
scale = (float) (image_->getWidth() - 1 - fromX) / (float) (otherToX - fromX);
|
|
otherToX = image_->getWidth() - 1;
|
|
}
|
|
int otherToY = (int) (fromY - (toY - fromY) * scale);
|
|
|
|
scale = 1.0f;
|
|
if (otherToY < 0) {
|
|
scale = (float) fromY / (float) (fromY - otherToY);
|
|
otherToY = 0;
|
|
} else if (otherToY >= (int)image_->getHeight()) {
|
|
scale = (float) (image_->getHeight() - 1 - fromY) / (float) (otherToY - fromY);
|
|
otherToY = image_->getHeight() - 1;
|
|
}
|
|
otherToX = (int) (fromX + (otherToX - fromX) * scale);
|
|
|
|
result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
|
|
|
|
// Middle pixel is double-counted this way; subtract 1
|
|
return result - 1.0f;
|
|
}
|
|
|
|
float Detector::sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) {
|
|
// Mild variant of Bresenham's algorithm;
|
|
// see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
|
|
bool steep = abs(toY - fromY) > abs(toX - fromX);
|
|
if (steep) {
|
|
int temp = fromX;
|
|
fromX = fromY;
|
|
fromY = temp;
|
|
temp = toX;
|
|
toX = toY;
|
|
toY = temp;
|
|
}
|
|
|
|
int dx = abs(toX - fromX);
|
|
int dy = abs(toY - fromY);
|
|
int error = -dx >> 1;
|
|
int xstep = fromX < toX ? 1 : -1;
|
|
int ystep = fromY < toY ? 1 : -1;
|
|
|
|
// In black pixels, looking for white, first or second time.
|
|
int state = 0;
|
|
// Loop up until x == toX, but not beyond
|
|
int xLimit = toX + xstep;
|
|
for (int x = fromX, y = fromY; x != xLimit; x += xstep) {
|
|
int realX = steep ? y : x;
|
|
int realY = steep ? x : y;
|
|
|
|
// Does current pixel mean we have moved white to black or vice versa?
|
|
if (!((state == 1) ^ image_->get(realX, realY))) {
|
|
if (state == 2) {
|
|
int diffX = x - fromX;
|
|
int diffY = y - fromY;
|
|
return (float) sqrt((double) (diffX * diffX + diffY * diffY));
|
|
}
|
|
state++;
|
|
}
|
|
|
|
error += dy;
|
|
if (error > 0) {
|
|
if (y == toY) {
|
|
break;
|
|
}
|
|
y += ystep;
|
|
error -= dx;
|
|
}
|
|
}
|
|
// Found black-white-black; give the benefit of the doubt that the next pixel outside the image
|
|
// is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a
|
|
// small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.
|
|
if (state == 2) {
|
|
int diffX = toX + xstep - fromX;
|
|
int diffY = toY - fromY;
|
|
return (float) sqrt((double) (diffX * diffX + diffY * diffY));
|
|
}
|
|
// else we didn't find even black-white-black; no estimate is really possible
|
|
return NAN;
|
|
}
|
|
|
|
Ref<AlignmentPattern> Detector::findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY,
|
|
float allowanceFactor) {
|
|
// Look for an alignment pattern (3 modules in size) around where it
|
|
// should be
|
|
int allowance = (int)(allowanceFactor * overallEstModuleSize);
|
|
int alignmentAreaLeftX = max(0, estAlignmentX - allowance);
|
|
int alignmentAreaRightX = min((int)(image_->getWidth() - 1), estAlignmentX + allowance);
|
|
if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {
|
|
throw zxing::ReaderException("region too small to hold alignment pattern");
|
|
}
|
|
int alignmentAreaTopY = max(0, estAlignmentY - allowance);
|
|
int alignmentAreaBottomY = min((int)(image_->getHeight() - 1), estAlignmentY + allowance);
|
|
if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) {
|
|
throw zxing::ReaderException("region too small to hold alignment pattern");
|
|
}
|
|
|
|
AlignmentPatternFinder alignmentFinder(image_, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX
|
|
- alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, callback_);
|
|
return alignmentFinder.find();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/detector/FinderPattern.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* FinderPattern.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 13/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/detector/FinderPattern.h>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
FinderPattern::FinderPattern(float posX, float posY, float estimatedModuleSize) :
|
|
ResultPoint(posX,posY), estimatedModuleSize_(estimatedModuleSize), count_(1) {
|
|
}
|
|
|
|
FinderPattern::FinderPattern(float posX, float posY, float estimatedModuleSize, int count) :
|
|
ResultPoint(posX,posY), estimatedModuleSize_(estimatedModuleSize), count_(count) {
|
|
}
|
|
|
|
int FinderPattern::getCount() const {
|
|
return count_;
|
|
}
|
|
|
|
float FinderPattern::getEstimatedModuleSize() const {
|
|
return estimatedModuleSize_;
|
|
}
|
|
|
|
void FinderPattern::incrementCount() {
|
|
count_++;
|
|
}
|
|
|
|
/*
|
|
bool FinderPattern::aboutEquals(float moduleSize, float i, float j) const {
|
|
return abs(i - posY_) <= moduleSize && abs(j - posX_) <= moduleSize && (abs(moduleSize - estimatedModuleSize_)
|
|
<= 1.0f || abs(moduleSize - estimatedModuleSize_) / estimatedModuleSize_ <= 0.1f);
|
|
}
|
|
*/
|
|
bool FinderPattern::aboutEquals(float moduleSize, float i, float j) const {
|
|
if (abs(i - getY()) <= moduleSize && abs(j - getX()) <= moduleSize) {
|
|
float moduleSizeDiff = abs(moduleSize - estimatedModuleSize_);
|
|
return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize_;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Ref<FinderPattern> FinderPattern::combineEstimate(float i, float j, float newModuleSize) const {
|
|
int combinedCount = count_ + 1;
|
|
float combinedX = (count_ * getX() + j) / combinedCount;
|
|
float combinedY = (count_ * getY() + i) / combinedCount;
|
|
float combinedModuleSize = (count_ * getEstimatedModuleSize() + newModuleSize) / combinedCount;
|
|
return Ref<FinderPattern>(new FinderPattern(combinedX, combinedY, combinedModuleSize, combinedCount));
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/detector/FinderPatternFinder.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* FinderPatternFinder.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 13/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/detector/FinderPatternFinder.h>
|
|
// #include <zxing/ReaderException.h>
|
|
// #include <zxing/DecodeHints.h>
|
|
// #include <vector>
|
|
// #include <cmath>
|
|
// #include <cstdlib>
|
|
// #include <algorithm>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
using namespace std;
|
|
|
|
class FurthestFromAverageComparator {
|
|
private:
|
|
const float averageModuleSize_;
|
|
public:
|
|
FurthestFromAverageComparator(float averageModuleSize) :
|
|
averageModuleSize_(averageModuleSize) {
|
|
}
|
|
bool operator()(Ref<FinderPattern> a, Ref<FinderPattern> b) {
|
|
float dA = abs(a->getEstimatedModuleSize() - averageModuleSize_);
|
|
float dB = abs(b->getEstimatedModuleSize() - averageModuleSize_);
|
|
return dA > dB;
|
|
}
|
|
};
|
|
|
|
class CenterComparator {
|
|
const float averageModuleSize_;
|
|
public:
|
|
CenterComparator(float averageModuleSize) :
|
|
averageModuleSize_(averageModuleSize) {
|
|
}
|
|
bool operator()(Ref<FinderPattern> a, Ref<FinderPattern> b) {
|
|
// N.B.: we want the result in descending order ...
|
|
if (a->getCount() != b->getCount()) {
|
|
return a->getCount() > b->getCount();
|
|
} else {
|
|
float dA = abs(a->getEstimatedModuleSize() - averageModuleSize_);
|
|
float dB = abs(b->getEstimatedModuleSize() - averageModuleSize_);
|
|
return dA < dB;
|
|
}
|
|
}
|
|
};
|
|
|
|
int FinderPatternFinder::CENTER_QUORUM = 2;
|
|
int FinderPatternFinder::MIN_SKIP = 3;
|
|
int FinderPatternFinder::MAX_MODULES = 57;
|
|
|
|
float FinderPatternFinder::centerFromEnd(int* stateCount, int end) {
|
|
return (float)(end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f;
|
|
}
|
|
|
|
bool FinderPatternFinder::foundPatternCross(int* stateCount) {
|
|
int totalModuleSize = 0;
|
|
for (int i = 0; i < 5; i++) {
|
|
if (stateCount[i] == 0) {
|
|
return false;
|
|
}
|
|
totalModuleSize += stateCount[i];
|
|
}
|
|
if (totalModuleSize < 7) {
|
|
return false;
|
|
}
|
|
float moduleSize = (float)totalModuleSize / 7.0f;
|
|
float maxVariance = moduleSize / 2.0f;
|
|
// Allow less than 50% variance from 1-1-3-1-1 proportions
|
|
return abs(moduleSize - stateCount[0]) < maxVariance && abs(moduleSize - stateCount[1]) < maxVariance && abs(3.0f
|
|
* moduleSize - stateCount[2]) < 3.0f * maxVariance && abs(moduleSize - stateCount[3]) < maxVariance && abs(
|
|
moduleSize - stateCount[4]) < maxVariance;
|
|
}
|
|
|
|
float FinderPatternFinder::crossCheckVertical(size_t startI, size_t centerJ, int maxCount, int originalStateCountTotal) {
|
|
|
|
int maxI = image_->getHeight();
|
|
int stateCount[5];
|
|
for (int i = 0; i < 5; i++)
|
|
stateCount[i] = 0;
|
|
|
|
|
|
// Start counting up from center
|
|
int i = startI;
|
|
while (i >= 0 && image_->get(centerJ, i)) {
|
|
stateCount[2]++;
|
|
i--;
|
|
}
|
|
if (i < 0) {
|
|
return NAN;
|
|
}
|
|
while (i >= 0 && !image_->get(centerJ, i) && stateCount[1] <= maxCount) {
|
|
stateCount[1]++;
|
|
i--;
|
|
}
|
|
// If already too many modules in this state or ran off the edge:
|
|
if (i < 0 || stateCount[1] > maxCount) {
|
|
return NAN;
|
|
}
|
|
while (i >= 0 && image_->get(centerJ, i) && stateCount[0] <= maxCount) {
|
|
stateCount[0]++;
|
|
i--;
|
|
}
|
|
if (stateCount[0] > maxCount) {
|
|
return NAN;
|
|
}
|
|
|
|
// Now also count down from center
|
|
i = startI + 1;
|
|
while (i < maxI && image_->get(centerJ, i)) {
|
|
stateCount[2]++;
|
|
i++;
|
|
}
|
|
if (i == maxI) {
|
|
return NAN;
|
|
}
|
|
while (i < maxI && !image_->get(centerJ, i) && stateCount[3] < maxCount) {
|
|
stateCount[3]++;
|
|
i++;
|
|
}
|
|
if (i == maxI || stateCount[3] >= maxCount) {
|
|
return NAN;
|
|
}
|
|
while (i < maxI && image_->get(centerJ, i) && stateCount[4] < maxCount) {
|
|
stateCount[4]++;
|
|
i++;
|
|
}
|
|
if (stateCount[4] >= maxCount) {
|
|
return NAN;
|
|
}
|
|
|
|
// If we found a finder-pattern-like section, but its size is more than 40% different than
|
|
// the original, assume it's a false positive
|
|
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
|
|
if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
|
|
return NAN;
|
|
}
|
|
|
|
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : NAN;
|
|
}
|
|
|
|
float FinderPatternFinder::crossCheckHorizontal(size_t startJ, size_t centerI, int maxCount,
|
|
int originalStateCountTotal) {
|
|
|
|
int maxJ = image_->getWidth();
|
|
int stateCount[5];
|
|
for (int i = 0; i < 5; i++)
|
|
stateCount[i] = 0;
|
|
|
|
int j = startJ;
|
|
while (j >= 0 && image_->get(j, centerI)) {
|
|
stateCount[2]++;
|
|
j--;
|
|
}
|
|
if (j < 0) {
|
|
return NAN;
|
|
}
|
|
while (j >= 0 && !image_->get(j, centerI) && stateCount[1] <= maxCount) {
|
|
stateCount[1]++;
|
|
j--;
|
|
}
|
|
if (j < 0 || stateCount[1] > maxCount) {
|
|
return NAN;
|
|
}
|
|
while (j >= 0 && image_->get(j, centerI) && stateCount[0] <= maxCount) {
|
|
stateCount[0]++;
|
|
j--;
|
|
}
|
|
if (stateCount[0] > maxCount) {
|
|
return NAN;
|
|
}
|
|
|
|
j = startJ + 1;
|
|
while (j < maxJ && image_->get(j, centerI)) {
|
|
stateCount[2]++;
|
|
j++;
|
|
}
|
|
if (j == maxJ) {
|
|
return NAN;
|
|
}
|
|
while (j < maxJ && !image_->get(j, centerI) && stateCount[3] < maxCount) {
|
|
stateCount[3]++;
|
|
j++;
|
|
}
|
|
if (j == maxJ || stateCount[3] >= maxCount) {
|
|
return NAN;
|
|
}
|
|
while (j < maxJ && image_->get(j, centerI) && stateCount[4] < maxCount) {
|
|
stateCount[4]++;
|
|
j++;
|
|
}
|
|
if (stateCount[4] >= maxCount) {
|
|
return NAN;
|
|
}
|
|
|
|
// If we found a finder-pattern-like section, but its size is significantly different than
|
|
// the original, assume it's a false positive
|
|
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
|
|
if (5 * abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {
|
|
return NAN;
|
|
}
|
|
|
|
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : NAN;
|
|
}
|
|
|
|
bool FinderPatternFinder::handlePossibleCenter(int* stateCount, size_t i, size_t j) {
|
|
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
|
|
float centerJ = centerFromEnd(stateCount, j);
|
|
float centerI = crossCheckVertical(i, (size_t)centerJ, stateCount[2], stateCountTotal);
|
|
if (!isnan(centerI)) {
|
|
// Re-cross check
|
|
centerJ = crossCheckHorizontal((size_t)centerJ, (size_t)centerI, stateCount[2], stateCountTotal);
|
|
if (!isnan(centerJ)) {
|
|
float estimatedModuleSize = (float)stateCountTotal / 7.0f;
|
|
bool found = false;
|
|
size_t max = possibleCenters_.size();
|
|
for (size_t index = 0; index < max; index++) {
|
|
Ref<FinderPattern> center = possibleCenters_[index];
|
|
// Look for about the same center and module size:
|
|
if (center->aboutEquals(estimatedModuleSize, centerI, centerJ)) {
|
|
possibleCenters_[index] = center->combineEstimate(centerI, centerJ, estimatedModuleSize);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
Ref<FinderPattern> newPattern(new FinderPattern(centerJ, centerI, estimatedModuleSize));
|
|
possibleCenters_.push_back(newPattern);
|
|
if (callback_ != 0) {
|
|
callback_->foundPossibleResultPoint(*newPattern);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int FinderPatternFinder::findRowSkip() {
|
|
size_t max = possibleCenters_.size();
|
|
if (max <= 1) {
|
|
return 0;
|
|
}
|
|
Ref<FinderPattern> firstConfirmedCenter;
|
|
for (size_t i = 0; i < max; i++) {
|
|
Ref<FinderPattern> center = possibleCenters_[i];
|
|
if (center->getCount() >= CENTER_QUORUM) {
|
|
if (firstConfirmedCenter == 0) {
|
|
firstConfirmedCenter = center;
|
|
} else {
|
|
// We have two confirmed centers
|
|
// How far down can we skip before resuming looking for the next
|
|
// pattern? In the worst case, only the difference between the
|
|
// difference in the x / y coordinates of the two centers.
|
|
// This is the case where you find top left first. Draw it out.
|
|
hasSkipped_ = true;
|
|
return (int)(abs(firstConfirmedCenter->getX() - center->getX()) - abs(firstConfirmedCenter->getY()
|
|
- center->getY()))/2;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool FinderPatternFinder::haveMultiplyConfirmedCenters() {
|
|
int confirmedCount = 0;
|
|
float totalModuleSize = 0.0f;
|
|
size_t max = possibleCenters_.size();
|
|
for (size_t i = 0; i < max; i++) {
|
|
Ref<FinderPattern> pattern = possibleCenters_[i];
|
|
if (pattern->getCount() >= CENTER_QUORUM) {
|
|
confirmedCount++;
|
|
totalModuleSize += pattern->getEstimatedModuleSize();
|
|
}
|
|
}
|
|
if (confirmedCount < 3) {
|
|
return false;
|
|
}
|
|
// OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
|
|
// and that we need to keep looking. We detect this by asking if the estimated module sizes
|
|
// vary too much. We arbitrarily say that when the total deviation from average exceeds
|
|
// 5% of the total module size estimates, it's too much.
|
|
float average = totalModuleSize / max;
|
|
float totalDeviation = 0.0f;
|
|
for (size_t i = 0; i < max; i++) {
|
|
Ref<FinderPattern> pattern = possibleCenters_[i];
|
|
totalDeviation += abs(pattern->getEstimatedModuleSize() - average);
|
|
}
|
|
return totalDeviation <= 0.05f * totalModuleSize;
|
|
}
|
|
|
|
vector<Ref<FinderPattern> > FinderPatternFinder::selectBestPatterns() {
|
|
size_t startSize = possibleCenters_.size();
|
|
|
|
if (startSize < 3) {
|
|
// Couldn't find enough finder patterns
|
|
throw zxing::ReaderException("Could not find three finder patterns");
|
|
}
|
|
|
|
// Filter outlier possibilities whose module size is too different
|
|
if (startSize > 3) {
|
|
// But we can only afford to do so if we have at least 4 possibilities to choose from
|
|
float totalModuleSize = 0.0f;
|
|
float square = 0.0f;
|
|
for (size_t i = 0; i < startSize; i++) {
|
|
float size = possibleCenters_[i]->getEstimatedModuleSize();
|
|
totalModuleSize += size;
|
|
square += size * size;
|
|
}
|
|
float average = totalModuleSize / (float) startSize;
|
|
float stdDev = (float)sqrt(square / startSize - average * average);
|
|
|
|
sort(possibleCenters_.begin(), possibleCenters_.end(), FurthestFromAverageComparator(average));
|
|
|
|
float limit = max(0.2f * average, stdDev);
|
|
|
|
for (size_t i = 0; i < possibleCenters_.size() && possibleCenters_.size() > 3; i++) {
|
|
if (abs(possibleCenters_[i]->getEstimatedModuleSize() - average) > limit) {
|
|
possibleCenters_.erase(possibleCenters_.begin()+i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (possibleCenters_.size() > 3) {
|
|
// Throw away all but those first size candidate points we found.
|
|
float totalModuleSize = 0.0f;
|
|
for (size_t i = 0; i < possibleCenters_.size(); i++) {
|
|
float size = possibleCenters_[i]->getEstimatedModuleSize();
|
|
totalModuleSize += size;
|
|
}
|
|
float average = totalModuleSize / (float) possibleCenters_.size();
|
|
sort(possibleCenters_.begin(), possibleCenters_.end(), CenterComparator(average));
|
|
}
|
|
|
|
if (possibleCenters_.size() > 3) {
|
|
possibleCenters_.erase(possibleCenters_.begin()+3,possibleCenters_.end());
|
|
}
|
|
|
|
vector<Ref<FinderPattern> > result(3);
|
|
result[0] = possibleCenters_[0];
|
|
result[1] = possibleCenters_[1];
|
|
result[2] = possibleCenters_[2];
|
|
return result;
|
|
}
|
|
|
|
vector<Ref<FinderPattern> > FinderPatternFinder::orderBestPatterns(vector<Ref<FinderPattern> > patterns) {
|
|
// Find distances between pattern centers
|
|
float abDistance = distance(patterns[0], patterns[1]);
|
|
float bcDistance = distance(patterns[1], patterns[2]);
|
|
float acDistance = distance(patterns[0], patterns[2]);
|
|
|
|
Ref<FinderPattern> topLeft;
|
|
Ref<FinderPattern> topRight;
|
|
Ref<FinderPattern> bottomLeft;
|
|
// Assume one closest to other two is top left;
|
|
// topRight and bottomLeft will just be guesses below at first
|
|
if (bcDistance >= abDistance && bcDistance >= acDistance) {
|
|
topLeft = patterns[0];
|
|
topRight = patterns[1];
|
|
bottomLeft = patterns[2];
|
|
} else if (acDistance >= bcDistance && acDistance >= abDistance) {
|
|
topLeft = patterns[1];
|
|
topRight = patterns[0];
|
|
bottomLeft = patterns[2];
|
|
} else {
|
|
topLeft = patterns[2];
|
|
topRight = patterns[0];
|
|
bottomLeft = patterns[1];
|
|
}
|
|
|
|
// Use cross product to figure out which of other1/2 is the bottom left
|
|
// pattern. The vector "top-left -> bottom-left" x "top-left -> top-right"
|
|
// should yield a vector with positive z component
|
|
if ((bottomLeft->getY() - topLeft->getY()) * (topRight->getX() - topLeft->getX()) < (bottomLeft->getX()
|
|
- topLeft->getX()) * (topRight->getY() - topLeft->getY())) {
|
|
Ref<FinderPattern> temp = topRight;
|
|
topRight = bottomLeft;
|
|
bottomLeft = temp;
|
|
}
|
|
|
|
vector<Ref<FinderPattern> > results(3);
|
|
results[0] = bottomLeft;
|
|
results[1] = topLeft;
|
|
results[2] = topRight;
|
|
return results;
|
|
}
|
|
|
|
float FinderPatternFinder::distance(Ref<ResultPoint> p1, Ref<ResultPoint> p2) {
|
|
float dx = p1->getX() - p2->getX();
|
|
float dy = p1->getY() - p2->getY();
|
|
return (float)sqrt(dx * dx + dy * dy);
|
|
}
|
|
|
|
FinderPatternFinder::FinderPatternFinder(Ref<BitMatrix> image,
|
|
Ref<ResultPointCallback>const& callback) :
|
|
image_(image), possibleCenters_(), hasSkipped_(false), callback_(callback) {
|
|
}
|
|
|
|
Ref<FinderPatternInfo> FinderPatternFinder::find(DecodeHints const& hints) {
|
|
bool tryHarder = hints.getTryHarder();
|
|
|
|
size_t maxI = image_->getHeight();
|
|
size_t maxJ = image_->getWidth();
|
|
|
|
|
|
// We are looking for black/white/black/white/black modules in
|
|
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
|
|
|
|
// As this is used often, we use an integer array instead of vector
|
|
int stateCount[5];
|
|
bool done = false;
|
|
|
|
|
|
// Let's assume that the maximum version QR Code we support takes up 1/4
|
|
// the height of the image, and then account for the center being 3
|
|
// modules in size. This gives the smallest number of pixels the center
|
|
// could be, so skip this often. When trying harder, look for all
|
|
// QR versions regardless of how dense they are.
|
|
int iSkip = (3 * maxI) / (4 * MAX_MODULES);
|
|
if (iSkip < MIN_SKIP || tryHarder) {
|
|
iSkip = MIN_SKIP;
|
|
}
|
|
|
|
// This is slightly faster than using the Ref. Efficiency is important here
|
|
BitMatrix& matrix = *image_;
|
|
|
|
for (size_t i = iSkip - 1; i < maxI && !done; i += iSkip) {
|
|
// Get a row of black/white values
|
|
|
|
stateCount[0] = 0;
|
|
stateCount[1] = 0;
|
|
stateCount[2] = 0;
|
|
stateCount[3] = 0;
|
|
stateCount[4] = 0;
|
|
int currentState = 0;
|
|
for (size_t j = 0; j < maxJ; j++) {
|
|
if (matrix.get(j, i)) {
|
|
// Black pixel
|
|
if ((currentState & 1) == 1) { // Counting white pixels
|
|
currentState++;
|
|
}
|
|
stateCount[currentState]++;
|
|
} else { // White pixel
|
|
if ((currentState & 1) == 0) { // Counting black pixels
|
|
if (currentState == 4) { // A winner?
|
|
if (foundPatternCross(stateCount)) { // Yes
|
|
bool confirmed = handlePossibleCenter(stateCount, i, j);
|
|
if (confirmed) {
|
|
// Start examining every other line. Checking each line turned out to be too
|
|
// expensive and didn't improve performance.
|
|
iSkip = 2;
|
|
if (hasSkipped_) {
|
|
done = haveMultiplyConfirmedCenters();
|
|
} else {
|
|
int rowSkip = findRowSkip();
|
|
if (rowSkip > stateCount[2]) {
|
|
// Skip rows between row of lower confirmed center
|
|
// and top of presumed third confirmed center
|
|
// but back up a bit to get a full chance of detecting
|
|
// it, entire width of center of finder pattern
|
|
|
|
// Skip by rowSkip, but back off by stateCount[2] (size
|
|
// of last center of pattern we saw) to be conservative,
|
|
// and also back off by iSkip which is about to be
|
|
// re-added
|
|
i += rowSkip - stateCount[2] - iSkip;
|
|
j = maxJ - 1;
|
|
}
|
|
}
|
|
} else {
|
|
stateCount[0] = stateCount[2];
|
|
stateCount[1] = stateCount[3];
|
|
stateCount[2] = stateCount[4];
|
|
stateCount[3] = 1;
|
|
stateCount[4] = 0;
|
|
currentState = 3;
|
|
continue;
|
|
}
|
|
// Clear state to start looking again
|
|
currentState = 0;
|
|
stateCount[0] = 0;
|
|
stateCount[1] = 0;
|
|
stateCount[2] = 0;
|
|
stateCount[3] = 0;
|
|
stateCount[4] = 0;
|
|
} else { // No, shift counts back by two
|
|
stateCount[0] = stateCount[2];
|
|
stateCount[1] = stateCount[3];
|
|
stateCount[2] = stateCount[4];
|
|
stateCount[3] = 1;
|
|
stateCount[4] = 0;
|
|
currentState = 3;
|
|
}
|
|
} else {
|
|
stateCount[++currentState]++;
|
|
}
|
|
} else { // Counting white pixels
|
|
stateCount[currentState]++;
|
|
}
|
|
}
|
|
}
|
|
if (foundPatternCross(stateCount)) {
|
|
bool confirmed = handlePossibleCenter(stateCount, i, maxJ);
|
|
if (confirmed) {
|
|
iSkip = stateCount[0];
|
|
if (hasSkipped_) {
|
|
// Found a third one
|
|
done = haveMultiplyConfirmedCenters();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<Ref<FinderPattern> > patternInfo = selectBestPatterns();
|
|
patternInfo = orderBestPatterns(patternInfo);
|
|
|
|
Ref<FinderPatternInfo> result(new FinderPatternInfo(patternInfo));
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/detector/FinderPatternInfo.cpp
|
|
|
|
/*
|
|
* FinderPatternInfo.cpp
|
|
* zxing
|
|
*
|
|
* Created by Christian Brunschen on 13/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/detector/FinderPatternInfo.h>
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
FinderPatternInfo::FinderPatternInfo(std::vector<Ref<FinderPattern> > patternCenters) :
|
|
bottomLeft_(patternCenters[0]), topLeft_(patternCenters[1]), topRight_(patternCenters[2]) {
|
|
}
|
|
|
|
Ref<FinderPattern> FinderPatternInfo::getBottomLeft() {
|
|
return bottomLeft_;
|
|
}
|
|
Ref<FinderPattern> FinderPatternInfo::getTopLeft() {
|
|
return topLeft_;
|
|
}
|
|
Ref<FinderPattern> FinderPatternInfo::getTopRight() {
|
|
return topRight_;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// file: zxing/qrcode/detector/QREdgeDetector.cpp
|
|
|
|
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
|
/*
|
|
* Created by Ralf Kistner on 7/12/2009.
|
|
* 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/detector/QREdgeDetector.h>
|
|
// #include <zxing/common/EdgeDetector.h>
|
|
// #include <cstdlib>
|
|
|
|
using namespace std;
|
|
|
|
namespace zxing {
|
|
namespace qrcode {
|
|
|
|
static const float patternEdgeThreshold = 2;
|
|
static const int patternEdgeWidth = 3;
|
|
static const float patternEdgeSearchRatio = 1.1;
|
|
static const int patternEdgeSkip = 2;
|
|
|
|
static const float accurateEdgeThreshold = 3.3;
|
|
static const int accurateEdgeWidth = 7;
|
|
static const int accurateEdgeSkip = 2;
|
|
|
|
static Point guessLastPattern(Point topLeft, Point topRight, Point bottomLeft) {
|
|
return Point(topRight.x - topLeft.x + bottomLeft.x, topRight.y - topLeft.y + bottomLeft.y);
|
|
}
|
|
|
|
static Point rp(Ref<ResultPoint> rp) {
|
|
return Point(rp->getX(), rp->getY());
|
|
}
|
|
|
|
QREdgeDetector::QREdgeDetector(Ref<BitMatrix> image) : Detector(image) { }
|
|
|
|
Ref<PerspectiveTransform> QREdgeDetector::createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
|
|
ResultPoint > bottomLeft, Ref<ResultPoint> alignmentPattern, int dimension) {
|
|
|
|
if(alignmentPattern == NULL) {
|
|
Point corner = findCorner(*Detector::getImage(), rp(topLeft), rp(topRight), rp(bottomLeft), dimension);
|
|
return get1CornerTransform(rp(topLeft), rp(topRight), rp(bottomLeft), corner, dimension);
|
|
} else {
|
|
return Detector::createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
Point QREdgeDetector::findCorner(const BitMatrix& image, Point topLeft, Point topRight, Point bottomLeft, int dimension) {
|
|
(void)dimension;
|
|
Point bottomRight = guessLastPattern(topLeft, topRight, bottomLeft);
|
|
|
|
Line bottomEst = findPatternEdge(image, bottomLeft, topLeft, bottomRight, false);
|
|
Line rightEst = findPatternEdge(image, topRight, topLeft, bottomRight, true);
|
|
|
|
//return EdgeDetector::intersection(bottomEst, rightEst);
|
|
|
|
Line bottom = EdgeDetector::findLine(image, bottomEst, false, accurateEdgeWidth, accurateEdgeThreshold, accurateEdgeSkip);
|
|
Line right = EdgeDetector::findLine(image, rightEst, true, accurateEdgeWidth, accurateEdgeThreshold, accurateEdgeSkip);
|
|
|
|
|
|
return EdgeDetector::intersection(bottom, right);
|
|
}
|
|
|
|
Line QREdgeDetector::findPatternEdge(const BitMatrix& image, Point pattern, Point opposite, Point direction, bool invert) {
|
|
Point start = endOfReverseBlackWhiteBlackRun(image, pattern, opposite);
|
|
|
|
float dx = pattern.x - start.x;
|
|
float dy = pattern.y - start.y;
|
|
float dist = sqrt(dx*dx + dy*dy);
|
|
|
|
float dirX = direction.x - pattern.x;
|
|
float dirY = direction.y - pattern.y;
|
|
float dirSize = sqrt(dirX*dirX + dirY*dirY);
|
|
|
|
float nx = dirX/dirSize;
|
|
float ny = dirY/dirSize;
|
|
|
|
float search = dist * patternEdgeSearchRatio;
|
|
Point a(start.x + nx*search, start.y + ny*search);
|
|
Point b(start.x - nx*search, start.y - ny*search);
|
|
|
|
return EdgeDetector::findLine(image, Line(a, b), invert, patternEdgeWidth, patternEdgeThreshold, patternEdgeSkip);
|
|
}
|
|
|
|
|
|
Ref<PerspectiveTransform> QREdgeDetector::get1CornerTransform(Point topLeft, Point topRight, Point bottomLeft, Point corner, int dimension) {
|
|
float dimMinusThree = (float) dimension - 3.5f;
|
|
|
|
Ref<PerspectiveTransform> transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, dimension,
|
|
dimension, 3.5f, dimMinusThree, topLeft.x, topLeft.y, topRight.x,
|
|
topRight.y, corner.x, corner.y, bottomLeft.x, bottomLeft.y));
|
|
|
|
return transform;
|
|
}
|
|
|
|
// Adapted from "sizeOfBlackWhiteBlackRun" in zxing::qrcode::Detector
|
|
Point QREdgeDetector::endOfReverseBlackWhiteBlackRun(const BitMatrix& image, Point from, Point to) {
|
|
int fromX = (int)from.x;
|
|
int fromY = (int)from.y;
|
|
int toX = (int)to.x;
|
|
int toY = (int)to.y;
|
|
|
|
bool steep = abs(toY - fromY) > abs(toX - fromX);
|
|
if (steep) {
|
|
int temp = fromX;
|
|
fromX = fromY;
|
|
fromY = temp;
|
|
temp = toX;
|
|
toX = toY;
|
|
toY = temp;
|
|
}
|
|
|
|
int dx = abs(toX - fromX);
|
|
int dy = abs(toY - fromY);
|
|
int error = -dx >> 1;
|
|
int ystep = fromY < toY ? -1 : 1;
|
|
int xstep = fromX < toX ? -1 : 1;
|
|
int state = 0; // In black pixels, looking for white, first or second time
|
|
|
|
// In case there are no points, prepopulate to from
|
|
int realX = fromX;
|
|
int realY = fromY;
|
|
for (int x = fromX, y = fromY; x != toX; x += xstep) {
|
|
realX = steep ? y : x;
|
|
realY = steep ? x : y;
|
|
|
|
if(realX < 0 || realY < 0 || realX >= (int)image.getWidth() || realY >= (int)image.getHeight())
|
|
break;
|
|
|
|
if (state == 1) { // In white pixels, looking for black
|
|
if (image.get(realX, realY)) {
|
|
state++;
|
|
}
|
|
} else {
|
|
if (!image.get(realX, realY)) {
|
|
state++;
|
|
}
|
|
}
|
|
|
|
if (state == 3) { // Found black, white, black, and stumbled back onto white; done
|
|
return Point(realX, realY);
|
|
}
|
|
error += dy;
|
|
if (error > 0) {
|
|
y += ystep;
|
|
error -= dx;
|
|
}
|
|
}
|
|
|
|
// B-W-B run not found, return the last point visited.
|
|
return Point(realX, realY);
|
|
}
|
|
|
|
} // namespace qrcode
|
|
} // namespace zxing
|
|
|