|
@@ -0,0 +1,11982 @@
|
|
|
|
+
|
|
|
|
+#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
|
|
|
|
+
|