mirror of
https://github.com/zxing/zxing.git
synced 2025-01-12 19:57:27 -08:00
C++ 1D/cleanup first pass
git-svn-id: https://zxing.googlecode.com/svn/trunk@2603 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
4e0a75346d
commit
4d28115073
1
cpp/.gdbinit
Normal file
1
cpp/.gdbinit
Normal file
|
@ -0,0 +1 @@
|
|||
set history save on
|
|
@ -1,14 +1,12 @@
|
|||
{
|
||||
zxing::common::CharacterSetECI::init_tables
|
||||
Memcheck:Leak
|
||||
fun:malloc
|
||||
...
|
||||
fun:_ZN5zxing6common15CharacterSetECI11init_tablesEv
|
||||
}
|
||||
dlsym
|
||||
Memcheck:Value8
|
||||
fun:_simple_salloc
|
||||
fun:_ZN4dyld9mkstringfEPKcz
|
||||
|
||||
}
|
||||
{
|
||||
ImageMagick
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:AcquireImage
|
||||
dlsym
|
||||
Memcheck:Cond
|
||||
fun:_ZN4dyld9mkstringfEPKcz
|
||||
}
|
34
cpp/core/src/zxing/BarcodeFormat.cpp
Executable file → Normal file
34
cpp/core/src/zxing/BarcodeFormat.cpp
Executable file → Normal file
|
@ -18,20 +18,22 @@
|
|||
|
||||
#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",
|
||||
"AZTEC"
|
||||
const char* zxing::BarcodeFormat::barcodeFormatNames[] = {
|
||||
"AZTEC",
|
||||
"CODABAR",
|
||||
"CODE_39",
|
||||
"CODE_93",
|
||||
"CODE_128",
|
||||
"DATA_MATRIX",
|
||||
"EAN_8",
|
||||
"EAN_13",
|
||||
"ITF",
|
||||
"MAXICODE",
|
||||
"PDF_417",
|
||||
"QR_CODE",
|
||||
"RSS_14",
|
||||
"RSS_EXPANDED",
|
||||
"UPC_A",
|
||||
"UPC_E",
|
||||
"UPC_EAN_EXTENSION",
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -22,23 +22,41 @@
|
|||
*/
|
||||
|
||||
namespace zxing {
|
||||
|
||||
typedef enum BarcodeFormat {
|
||||
BarcodeFormat_None = 0,
|
||||
BarcodeFormat_QR_CODE,
|
||||
BarcodeFormat_DATA_MATRIX,
|
||||
BarcodeFormat_UPC_E,
|
||||
BarcodeFormat_UPC_A,
|
||||
BarcodeFormat_EAN_8,
|
||||
BarcodeFormat_EAN_13,
|
||||
BarcodeFormat_CODE_128,
|
||||
BarcodeFormat_CODE_39,
|
||||
BarcodeFormat_ITF,
|
||||
BarcodeFormat_AZTEC
|
||||
} BarcodeFormat;
|
||||
class BarcodeFormat;
|
||||
}
|
||||
|
||||
/* if you update the enum, please update the name in BarcodeFormat.cpp */
|
||||
extern const char *barcodeFormatNames[];
|
||||
class zxing::BarcodeFormat {
|
||||
public:
|
||||
enum Value {
|
||||
AZTEC,
|
||||
CODABAR,
|
||||
CODE_39,
|
||||
CODE_93,
|
||||
CODE_128,
|
||||
DATA_MATRIX,
|
||||
EAN_8,
|
||||
EAN_13,
|
||||
ITF,
|
||||
MAXICODE,
|
||||
PDF_417,
|
||||
QR_CODE,
|
||||
RSS_14,
|
||||
RSS_EXPANDED,
|
||||
UPC_A,
|
||||
UPC_E,
|
||||
UPC_EAN_EXTENSION,
|
||||
};
|
||||
|
||||
BarcodeFormat(Value v) : value(v) {}
|
||||
const Value value;
|
||||
operator Value () const {return value;}
|
||||
|
||||
/* if you update the enum, please update the name in BarcodeFormat.cpp */
|
||||
static char const* barcodeFormatNames[];
|
||||
};
|
||||
|
||||
namespace zxing {
|
||||
class BarcodeFormat;
|
||||
}
|
||||
|
||||
#endif // __BARCODE_FORMAT_H__
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* BinaryBitmap.cpp
|
||||
* zxing
|
||||
|
@ -19,49 +20,51 @@
|
|||
|
||||
#include <zxing/BinaryBitmap.h>
|
||||
|
||||
namespace zxing {
|
||||
using zxing::Ref;
|
||||
using zxing::BitArray;
|
||||
using zxing::BitMatrix;
|
||||
using zxing::LuminanceSource;
|
||||
using zxing::BinaryBitmap;
|
||||
|
||||
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())));
|
||||
}
|
||||
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())));
|
||||
}
|
||||
|
|
41
cpp/core/src/zxing/ChecksumException.cpp
Normal file
41
cpp/core/src/zxing/ChecksumException.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* ChecksumException.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/ChecksumException.h>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
ChecksumException::ChecksumException() {}
|
||||
|
||||
ChecksumException::ChecksumException(const char *msg) :
|
||||
ReaderException(msg) {
|
||||
}
|
||||
|
||||
ChecksumException::~ChecksumException() throw() {
|
||||
}
|
||||
|
||||
ChecksumException const&
|
||||
ChecksumException::getChecksumInstance() {
|
||||
static ChecksumException instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
35
cpp/core/src/zxing/ChecksumException.h
Normal file
35
cpp/core/src/zxing/ChecksumException.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
|
||||
#ifndef __CHECKSUM_EXCEPTION_H__
|
||||
#define __NOT_FOUND_EXCEPTION_H__
|
||||
|
||||
/*
|
||||
* 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/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
class ChecksumException : public ReaderException {
|
||||
typedef ReaderException Base;
|
||||
public:
|
||||
ChecksumException();
|
||||
ChecksumException(const char *msg);
|
||||
~ChecksumException() throw();
|
||||
static ChecksumException const& getChecksumInstance();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __CHECKSUM_EXCEPTION_H__
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* DecodeHintType.cpp
|
||||
* zxing
|
||||
|
@ -19,36 +20,37 @@
|
|||
|
||||
#include <zxing/DecodeHints.h>
|
||||
#include <zxing/common/IllegalArgumentException.h>
|
||||
namespace zxing {
|
||||
|
||||
using zxing::Ref;
|
||||
using zxing::ResultPointCallback;
|
||||
using zxing::DecodeHintType;
|
||||
using zxing::DecodeHints;
|
||||
|
||||
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);
|
||||
UPC_A_HINT |
|
||||
UPC_E_HINT |
|
||||
EAN_13_HINT |
|
||||
EAN_8_HINT |
|
||||
RSS_14_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);
|
||||
CODE_39_HINT |
|
||||
CODE_93_HINT |
|
||||
CODE_128_HINT |
|
||||
ITF_HINT |
|
||||
CODABAR_HINT |
|
||||
DecodeHints::PRODUCT_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_AZTEC_HINT |
|
||||
BARCODEFORMAT_QR_CODE_HINT);
|
||||
ONED_HINT |
|
||||
QR_CODE_HINT |
|
||||
DATA_MATRIX_HINT |
|
||||
AZTEC_HINT
|
||||
);
|
||||
|
||||
DecodeHints::DecodeHints() {
|
||||
hints = 0;
|
||||
|
@ -60,34 +62,46 @@ DecodeHints::DecodeHints(DecodeHintType init) {
|
|||
|
||||
void DecodeHints::addFormat(BarcodeFormat toadd) {
|
||||
switch (toadd) {
|
||||
case BarcodeFormat_AZTEC: hints |= BARCODEFORMAT_AZTEC_HINT; break;
|
||||
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");
|
||||
case BarcodeFormat::AZTEC: hints |= AZTEC_HINT; break;
|
||||
case BarcodeFormat::CODABAR: hints |= CODABAR_HINT; break;
|
||||
case BarcodeFormat::CODE_39: hints |= CODE_39_HINT; break;
|
||||
case BarcodeFormat::CODE_93: hints |= CODE_93_HINT; break;
|
||||
case BarcodeFormat::CODE_128: hints |= CODE_128_HINT; break;
|
||||
case BarcodeFormat::DATA_MATRIX: hints |= DATA_MATRIX_HINT; break;
|
||||
case BarcodeFormat::EAN_8: hints |= EAN_8_HINT; break;
|
||||
case BarcodeFormat::EAN_13: hints |= EAN_13_HINT; break;
|
||||
case BarcodeFormat::ITF: hints |= ITF_HINT; break;
|
||||
case BarcodeFormat::MAXICODE: hints |= MAXICODE_HINT; break;
|
||||
case BarcodeFormat::QR_CODE: hints |= QR_CODE_HINT; break;
|
||||
case BarcodeFormat::RSS_14: hints |= RSS_14_HINT; break;
|
||||
case BarcodeFormat::RSS_EXPANDED: hints |= RSS_EXPANDED_HINT; break;
|
||||
case BarcodeFormat::UPC_A: hints |= UPC_A_HINT; break;
|
||||
case BarcodeFormat::UPC_E: hints |= UPC_E_HINT; break;
|
||||
case BarcodeFormat::UPC_EAN_EXTENSION: hints |= UPC_EAN_EXTENSION_HINT; break;
|
||||
default: throw IllegalArgumentException("Unrecognizd barcode format");
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeHints::containsFormat(BarcodeFormat tocheck) const {
|
||||
DecodeHintType checkAgainst;
|
||||
DecodeHintType checkAgainst = 0;
|
||||
switch (tocheck) {
|
||||
case BarcodeFormat_AZTEC: checkAgainst = BARCODEFORMAT_AZTEC_HINT; break;
|
||||
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");
|
||||
case BarcodeFormat::AZTEC: checkAgainst |= AZTEC_HINT; break;
|
||||
case BarcodeFormat::CODABAR: checkAgainst |= CODABAR_HINT; break;
|
||||
case BarcodeFormat::CODE_39: checkAgainst |= CODE_39_HINT; break;
|
||||
case BarcodeFormat::CODE_93: checkAgainst |= CODE_93_HINT; break;
|
||||
case BarcodeFormat::CODE_128: checkAgainst |= CODE_128_HINT; break;
|
||||
case BarcodeFormat::DATA_MATRIX: checkAgainst |= DATA_MATRIX_HINT; break;
|
||||
case BarcodeFormat::EAN_8: checkAgainst |= EAN_8_HINT; break;
|
||||
case BarcodeFormat::EAN_13: checkAgainst |= EAN_13_HINT; break;
|
||||
case BarcodeFormat::ITF: checkAgainst |= ITF_HINT; break;
|
||||
case BarcodeFormat::MAXICODE: checkAgainst |= MAXICODE_HINT; break;
|
||||
case BarcodeFormat::QR_CODE: checkAgainst |= QR_CODE_HINT; break;
|
||||
case BarcodeFormat::RSS_14: checkAgainst |= RSS_14_HINT; break;
|
||||
case BarcodeFormat::RSS_EXPANDED: checkAgainst |= RSS_EXPANDED_HINT; break;
|
||||
case BarcodeFormat::UPC_A: checkAgainst |= UPC_A_HINT; break;
|
||||
case BarcodeFormat::UPC_E: checkAgainst |= UPC_E_HINT; break;
|
||||
case BarcodeFormat::UPC_EAN_EXTENSION: checkAgainst |= UPC_EAN_EXTENSION_HINT; break;
|
||||
default: throw IllegalArgumentException("Unrecognizd barcode format");
|
||||
}
|
||||
return (hints & checkAgainst);
|
||||
}
|
||||
|
@ -105,11 +119,18 @@ bool DecodeHints::getTryHarder() const {
|
|||
}
|
||||
|
||||
void DecodeHints::setResultPointCallback(Ref<ResultPointCallback> const& _callback) {
|
||||
callback = _callback;
|
||||
callback = _callback;
|
||||
}
|
||||
|
||||
Ref<ResultPointCallback> DecodeHints::getResultPointCallback() const {
|
||||
return callback;
|
||||
return callback;
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
DecodeHints zxing::operator | (DecodeHints const& l, DecodeHints const& r) {
|
||||
DecodeHints result (l);
|
||||
result.hints |= r.hints;
|
||||
if (!result.callback) {
|
||||
result.callback = r.callback;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __DECODEHINTS_H_
|
||||
#define __DECODEHINTS_H_
|
||||
/*
|
||||
|
@ -23,29 +24,35 @@
|
|||
#include <zxing/ResultPointCallback.h>
|
||||
|
||||
namespace zxing {
|
||||
typedef unsigned int DecodeHintType;
|
||||
class DecodeHints;
|
||||
DecodeHints operator | (DecodeHints const&, DecodeHints const&);
|
||||
}
|
||||
|
||||
typedef unsigned int DecodeHintType;
|
||||
|
||||
class DecodeHints {
|
||||
|
||||
class zxing::DecodeHints {
|
||||
private:
|
||||
|
||||
DecodeHintType hints;
|
||||
|
||||
Ref<ResultPointCallback> callback;
|
||||
|
||||
public:
|
||||
static const DecodeHintType AZTEC_HINT = 1 << BarcodeFormat::AZTEC;
|
||||
static const DecodeHintType CODABAR_HINT = 1 << BarcodeFormat::CODABAR;
|
||||
static const DecodeHintType CODE_39_HINT = 1 << BarcodeFormat::CODE_39;
|
||||
static const DecodeHintType CODE_93_HINT = 1 << BarcodeFormat::CODE_93;
|
||||
static const DecodeHintType CODE_128_HINT = 1 << BarcodeFormat::CODE_128;
|
||||
static const DecodeHintType DATA_MATRIX_HINT = 1 << BarcodeFormat::DATA_MATRIX;
|
||||
static const DecodeHintType EAN_8_HINT = 1 << BarcodeFormat::EAN_8;
|
||||
static const DecodeHintType EAN_13_HINT = 1 << BarcodeFormat::EAN_13;
|
||||
static const DecodeHintType ITF_HINT = 1 << BarcodeFormat::ITF;
|
||||
static const DecodeHintType MAXICODE_HINT = 1 << BarcodeFormat::MAXICODE;
|
||||
static const DecodeHintType PDF_417_HINT = 1 << BarcodeFormat::PDF_417;
|
||||
static const DecodeHintType QR_CODE_HINT = 1 << BarcodeFormat::QR_CODE;
|
||||
static const DecodeHintType RSS_14_HINT = 1 << BarcodeFormat::RSS_14;
|
||||
static const DecodeHintType RSS_EXPANDED_HINT = 1 << BarcodeFormat::RSS_EXPANDED;
|
||||
static const DecodeHintType UPC_A_HINT = 1 << BarcodeFormat::UPC_A;
|
||||
static const DecodeHintType UPC_E_HINT = 1 << BarcodeFormat::UPC_E;
|
||||
static const DecodeHintType UPC_EAN_EXTENSION_HINT = 1 << BarcodeFormat::UPC_EAN_EXTENSION;
|
||||
|
||||
static const DecodeHintType BARCODEFORMAT_QR_CODE_HINT = 1 << BarcodeFormat_QR_CODE;
|
||||
static const DecodeHintType BARCODEFORMAT_DATA_MATRIX_HINT = 1 << BarcodeFormat_DATA_MATRIX;
|
||||
static const DecodeHintType BARCODEFORMAT_UPC_E_HINT = 1 << BarcodeFormat_UPC_E;
|
||||
static const DecodeHintType BARCODEFORMAT_UPC_A_HINT = 1 << BarcodeFormat_UPC_A;
|
||||
static const DecodeHintType BARCODEFORMAT_EAN_8_HINT = 1 << BarcodeFormat_EAN_8;
|
||||
static const DecodeHintType BARCODEFORMAT_EAN_13_HINT = 1 << BarcodeFormat_EAN_13;
|
||||
static const DecodeHintType BARCODEFORMAT_CODE_128_HINT = 1 << BarcodeFormat_CODE_128;
|
||||
static const DecodeHintType BARCODEFORMAT_CODE_39_HINT = 1 << BarcodeFormat_CODE_39;
|
||||
static const DecodeHintType BARCODEFORMAT_ITF_HINT = 1 << BarcodeFormat_ITF;
|
||||
static const DecodeHintType BARCODEFORMAT_AZTEC_HINT = 1 << BarcodeFormat_AZTEC;
|
||||
static const DecodeHintType CHARACTER_SET = 1 << 30;
|
||||
static const DecodeHintType TRYHARDER_HINT = 1 << 31;
|
||||
|
||||
|
@ -64,8 +71,7 @@ class DecodeHints {
|
|||
void setResultPointCallback(Ref<ResultPointCallback> const&);
|
||||
Ref<ResultPointCallback> getResultPointCallback() const;
|
||||
|
||||
friend DecodeHints operator | (DecodeHints const&, DecodeHints const&);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,4 +32,10 @@ FormatException::FormatException(const char *msg) :
|
|||
FormatException::~FormatException() throw() {
|
||||
}
|
||||
|
||||
FormatException const&
|
||||
FormatException::getFormatInstance() {
|
||||
static FormatException instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ public:
|
|||
FormatException();
|
||||
FormatException(const char *msg);
|
||||
~FormatException() throw();
|
||||
|
||||
static FormatException const& getFormatInstance();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
#include <zxing/LuminanceSource.h>
|
||||
#include <zxing/common/IllegalArgumentException.h>
|
||||
|
||||
namespace zxing {
|
||||
using zxing::Ref;
|
||||
using zxing::LuminanceSource;
|
||||
|
||||
LuminanceSource::LuminanceSource() {
|
||||
}
|
||||
|
@ -51,7 +52,7 @@ Ref<LuminanceSource> LuminanceSource::rotateCounterClockwise() {
|
|||
}
|
||||
|
||||
LuminanceSource::operator std::string() {
|
||||
unsigned char* row = 0;
|
||||
ArrayRef<char> row;
|
||||
std::ostringstream oss;
|
||||
for (int y = 0; y < getHeight(); y++) {
|
||||
row = getRow(y, row);
|
||||
|
@ -71,10 +72,5 @@ LuminanceSource::operator std::string() {
|
|||
}
|
||||
oss << '\n';
|
||||
}
|
||||
delete [] row;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -21,11 +21,14 @@
|
|||
*/
|
||||
|
||||
#include <zxing/common/Counted.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace zxing {
|
||||
class LuminanceSource;
|
||||
}
|
||||
|
||||
class LuminanceSource : public Counted {
|
||||
class zxing::LuminanceSource : public Counted {
|
||||
public:
|
||||
LuminanceSource();
|
||||
virtual ~LuminanceSource();
|
||||
|
@ -34,8 +37,8 @@ public:
|
|||
virtual int getHeight() const = 0;
|
||||
|
||||
// Callers take ownership of the returned memory and must call delete [] on it themselves.
|
||||
virtual unsigned char* getRow(int y, unsigned char* row) = 0;
|
||||
virtual unsigned char* getMatrix() = 0;
|
||||
virtual ArrayRef<char> getRow(int y, ArrayRef<char> row) = 0;
|
||||
virtual ArrayRef<char> getMatrix() = 0;
|
||||
|
||||
virtual bool isCropSupported() const;
|
||||
virtual Ref<LuminanceSource> crop(int left, int top, int width, int height);
|
||||
|
@ -44,9 +47,7 @@ public:
|
|||
virtual Ref<LuminanceSource> rotateCounterClockwise();
|
||||
|
||||
operator std::string (); // should be const but don't want to make sure a
|
||||
// large breaking change right now
|
||||
// large breaking change right now
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LUMINANCESOURCE_H_ */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* MultiFormatBarcodeReader.cpp
|
||||
* ZXing
|
||||
|
@ -27,80 +28,96 @@
|
|||
#include <zxing/oned/MultiFormatOneDReader.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
MultiFormatReader::MultiFormatReader() {
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
using zxing::MultiFormatReader;
|
||||
|
||||
}
|
||||
MultiFormatReader::MultiFormatReader() {}
|
||||
|
||||
Ref<Result> MultiFormatReader::decode(Ref<BinaryBitmap> image) {
|
||||
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);
|
||||
}
|
||||
return decodeInternal(image);
|
||||
}
|
||||
|
||||
Ref<Result> MultiFormatReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
||||
setHints(hints);
|
||||
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::UPC_E) ||
|
||||
hints.containsFormat(BarcodeFormat::EAN_13) ||
|
||||
hints.containsFormat(BarcodeFormat::EAN_8) ||
|
||||
hints.containsFormat(BarcodeFormat::CODABAR) ||
|
||||
hints.containsFormat(BarcodeFormat::CODE_39) ||
|
||||
hints.containsFormat(BarcodeFormat::CODE_93) ||
|
||||
hints.containsFormat(BarcodeFormat::CODE_128) ||
|
||||
hints.containsFormat(BarcodeFormat::ITF) ||
|
||||
hints.containsFormat(BarcodeFormat::RSS_14) ||
|
||||
hints.containsFormat(BarcodeFormat::RSS_EXPANDED);
|
||||
if (addOneDReader && !tryHarder) {
|
||||
readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints)));
|
||||
}
|
||||
|
||||
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);
|
||||
if (hints.containsFormat(BarcodeFormat::QR_CODE)) {
|
||||
readers_.push_back(Ref<Reader>(new zxing::qrcode::QRCodeReader()));
|
||||
}
|
||||
|
||||
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) {
|
||||
if (hints.containsFormat(BarcodeFormat::DATA_MATRIX)) {
|
||||
readers_.push_back(Ref<Reader>(new zxing::datamatrix::DataMatrixReader()));
|
||||
}
|
||||
if (hints.containsFormat(BarcodeFormat::AZTEC)) {
|
||||
readers_.push_back(Ref<Reader>(new zxing::aztec::AztecReader()));
|
||||
}
|
||||
/*
|
||||
if (formats.contains(BarcodeFormat.PDF_417)) {
|
||||
readers.add(new PDF417Reader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.MAXICODE)) {
|
||||
readers.add(new MaxiCodeReader());
|
||||
}
|
||||
*/
|
||||
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)));
|
||||
}
|
||||
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()));
|
||||
}
|
||||
if (hints.containsFormat(BarcodeFormat_AZTEC)) {
|
||||
readers_.push_back(Ref<Reader>(new zxing::aztec::AztecReader()));
|
||||
}
|
||||
//TODO: add PDF417 here once PDF417 reader is implemented
|
||||
if (addOneDReader && tryHarder) {
|
||||
readers_.push_back(Ref<Reader>(new zxing::qrcode::QRCodeReader()));
|
||||
readers_.push_back(Ref<Reader>(new zxing::datamatrix::DataMatrixReader()));
|
||||
readers_.push_back(Ref<Reader>(new zxing::aztec::AztecReader()));
|
||||
// readers.add(new PDF417Reader());
|
||||
// readers.add(new MaxiCodeReader());
|
||||
|
||||
if (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() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Result> MultiFormatReader::decodeInternal(Ref<BinaryBitmap> image) {
|
||||
for (unsigned int i = 0; i < readers_.size(); i++) {
|
||||
try {
|
||||
// std::cerr << "0 " << typeid(readers_[i]).name() << std::endl;
|
||||
return readers_[i]->decode(image, hints_);
|
||||
} catch (ReaderException const& re) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
throw ReaderException("No code detected");
|
||||
}
|
||||
|
||||
MultiFormatReader::~MultiFormatReader() {}
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
namespace zxing {
|
||||
class MultiFormatReader : public Reader {
|
||||
|
||||
private:
|
||||
Ref<Result> decodeInternal(Ref<BinaryBitmap> image);
|
||||
|
||||
|
|
|
@ -17,14 +17,18 @@
|
|||
|
||||
#include <zxing/NotFoundException.h>
|
||||
|
||||
namespace zxing {
|
||||
using zxing::NotFoundException;
|
||||
|
||||
NotFoundException::NotFoundException() {}
|
||||
NotFoundException::NotFoundException() {}
|
||||
|
||||
NotFoundException::NotFoundException(const char *msg)
|
||||
: ReaderException(msg) {}
|
||||
NotFoundException::NotFoundException(const char *msg)
|
||||
: ReaderException(msg) {}
|
||||
|
||||
NotFoundException::~NotFoundException() throw() {
|
||||
}
|
||||
NotFoundException::~NotFoundException() throw() {}
|
||||
|
||||
|
||||
NotFoundException const&
|
||||
NotFoundException::getNotFoundInstance() {
|
||||
static NotFoundException instance;
|
||||
return instance;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ namespace zxing {
|
|||
NotFoundException();
|
||||
NotFoundException(const char *msg);
|
||||
~NotFoundException() throw();
|
||||
|
||||
static NotFoundException const& getNotFoundInstance();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
0
cpp/core/src/zxing/Reader.cpp
Executable file → Normal file
0
cpp/core/src/zxing/Reader.cpp
Executable file → Normal file
0
cpp/core/src/zxing/Reader.h
Executable file → Normal file
0
cpp/core/src/zxing/Reader.h
Executable file → Normal file
|
@ -21,10 +21,15 @@
|
|||
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
using namespace std;
|
||||
using zxing::Result;
|
||||
using zxing::Ref;
|
||||
using zxing::ArrayRef;
|
||||
using zxing::String;
|
||||
using zxing::ResultPoint;
|
||||
|
||||
Result::Result(Ref<String> text, ArrayRef<unsigned char> rawBytes, std::vector<Ref<ResultPoint> > resultPoints,
|
||||
Result::Result(Ref<String> text,
|
||||
ArrayRef<char> rawBytes,
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints,
|
||||
BarcodeFormat format) :
|
||||
text_(text), rawBytes_(rawBytes), resultPoints_(resultPoints), format_(format) {
|
||||
}
|
||||
|
@ -36,29 +41,18 @@ Ref<String> Result::getText() {
|
|||
return text_;
|
||||
}
|
||||
|
||||
ArrayRef<unsigned char> Result::getRawBytes() {
|
||||
ArrayRef<char> Result::getRawBytes() {
|
||||
return rawBytes_;
|
||||
}
|
||||
|
||||
const std::vector<Ref<ResultPoint> >& Result::getResultPoints() const {
|
||||
ArrayRef< Ref<ResultPoint> > const& Result::getResultPoints() const {
|
||||
return resultPoints_;
|
||||
}
|
||||
|
||||
std::vector<Ref<ResultPoint> >& Result::getResultPoints() {
|
||||
ArrayRef< Ref<ResultPoint> >& Result::getResultPoints() {
|
||||
return resultPoints_;
|
||||
}
|
||||
|
||||
BarcodeFormat Result::getBarcodeFormat() const {
|
||||
zxing::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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/common/Counted.h>
|
||||
#include <zxing/common/Str.h>
|
||||
|
@ -33,18 +32,20 @@ namespace zxing {
|
|||
class Result : public Counted {
|
||||
private:
|
||||
Ref<String> text_;
|
||||
ArrayRef<unsigned char> rawBytes_;
|
||||
std::vector<Ref<ResultPoint> > resultPoints_;
|
||||
ArrayRef<char> rawBytes_;
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints_;
|
||||
BarcodeFormat format_;
|
||||
|
||||
public:
|
||||
Result(Ref<String> text, ArrayRef<unsigned char> rawBytes, std::vector<Ref<ResultPoint> > resultPoints,
|
||||
Result(Ref<String> text,
|
||||
ArrayRef<char> rawBytes,
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints,
|
||||
BarcodeFormat format);
|
||||
~Result();
|
||||
Ref<String> getText();
|
||||
ArrayRef<unsigned char> getRawBytes();
|
||||
const std::vector<Ref<ResultPoint> >& getResultPoints() const;
|
||||
std::vector<Ref<ResultPoint> >& getResultPoints();
|
||||
ArrayRef<char> getRawBytes();
|
||||
ArrayRef< Ref<ResultPoint> > const& getResultPoints() const;
|
||||
ArrayRef< Ref<ResultPoint> >& getResultPoints();
|
||||
BarcodeFormat getBarcodeFormat() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream &out, Result& result);
|
||||
|
|
34
cpp/core/src/zxing/ResultIO.cpp
Normal file
34
cpp/core/src/zxing/ResultIO.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* ResultIO.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>
|
||||
|
||||
using zxing::Result;
|
||||
using std::ostream;
|
||||
|
||||
ostream& zxing::operator<<(ostream &out, Result& result) {
|
||||
if (result.text_ != 0) {
|
||||
out << result.text_->getText();
|
||||
} else {
|
||||
out << "[" << result.rawBytes_->size() << " bytes]";
|
||||
}
|
||||
return out;
|
||||
}
|
0
cpp/core/src/zxing/ResultPoint.cpp
Executable file → Normal file
0
cpp/core/src/zxing/ResultPoint.cpp
Executable file → Normal file
|
@ -21,25 +21,27 @@
|
|||
|
||||
#include <zxing/aztec/AztecDetectorResult.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace aztec {
|
||||
AztecDetectorResult::AztecDetectorResult(Ref<BitMatrix> bits, std::vector<Ref<ResultPoint> > points, bool compact, int nbDatablocks, int nbLayers)
|
||||
: DetectorResult(bits, points),
|
||||
compact_(compact),
|
||||
nbDatablocks_(nbDatablocks),
|
||||
nbLayers_(nbLayers) {
|
||||
};
|
||||
using zxing::aztec::AztecDetectorResult;
|
||||
|
||||
AztecDetectorResult::AztecDetectorResult(Ref<BitMatrix> bits,
|
||||
ArrayRef< Ref<ResultPoint> > points,
|
||||
bool compact,
|
||||
int nbDatablocks,
|
||||
int nbLayers)
|
||||
: DetectorResult(bits, points),
|
||||
compact_(compact),
|
||||
nbDatablocks_(nbDatablocks),
|
||||
nbLayers_(nbLayers) {
|
||||
};
|
||||
|
||||
bool AztecDetectorResult::isCompact() {
|
||||
return compact_;
|
||||
}
|
||||
bool AztecDetectorResult::isCompact() {
|
||||
return compact_;
|
||||
}
|
||||
|
||||
int AztecDetectorResult::getNBDatablocks() {
|
||||
return nbDatablocks_;
|
||||
}
|
||||
int AztecDetectorResult::getNBDatablocks() {
|
||||
return nbDatablocks_;
|
||||
}
|
||||
|
||||
int AztecDetectorResult::getNBLayers() {
|
||||
return nbLayers_;
|
||||
}
|
||||
}
|
||||
}
|
||||
int AztecDetectorResult::getNBLayers() {
|
||||
return nbLayers_;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,11 @@ namespace zxing {
|
|||
bool compact_;
|
||||
int nbDatablocks_, nbLayers_;
|
||||
public:
|
||||
AztecDetectorResult(Ref<BitMatrix> bits, std::vector<Ref<ResultPoint> > points, bool compact, int nbDatablocks, int nbLayers);
|
||||
AztecDetectorResult(Ref<BitMatrix> bits,
|
||||
ArrayRef< Ref<ResultPoint> > points,
|
||||
bool compact,
|
||||
int nbDatablocks,
|
||||
int nbLayers);
|
||||
bool isCompact();
|
||||
int getNBDatablocks();
|
||||
int getNBLayers();
|
||||
|
|
|
@ -23,42 +23,41 @@
|
|||
#include <zxing/aztec/detector/Detector.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace zxing {
|
||||
namespace aztec {
|
||||
using zxing::Ref;
|
||||
using zxing::ArrayRef;
|
||||
using zxing::Result;
|
||||
using zxing::aztec::AztecReader;
|
||||
|
||||
AztecReader::AztecReader() : decoder_() {
|
||||
// nothing
|
||||
};
|
||||
AztecReader::AztecReader() : decoder_() {
|
||||
// nothing
|
||||
};
|
||||
|
||||
Ref<Result> AztecReader::decode(Ref<zxing::BinaryBitmap> image) {
|
||||
Detector detector(image->getBlackMatrix());
|
||||
Ref<Result> AztecReader::decode(Ref<zxing::BinaryBitmap> image) {
|
||||
Detector detector(image->getBlackMatrix());
|
||||
|
||||
Ref<AztecDetectorResult> detectorResult(detector.detect());
|
||||
Ref<AztecDetectorResult> detectorResult(detector.detect());
|
||||
|
||||
std::vector<Ref<ResultPoint> > points(detectorResult->getPoints());
|
||||
ArrayRef< Ref<ResultPoint> > points(detectorResult->getPoints());
|
||||
|
||||
Ref<DecoderResult> decoderResult(decoder_.decode(detectorResult));
|
||||
Ref<DecoderResult> decoderResult(decoder_.decode(detectorResult));
|
||||
|
||||
Ref<Result> result(new Result(decoderResult->getText(),
|
||||
decoderResult->getRawBytes(),
|
||||
points,
|
||||
BarcodeFormat_AZTEC));
|
||||
Ref<Result> result(new Result(decoderResult->getText(),
|
||||
decoderResult->getRawBytes(),
|
||||
points,
|
||||
BarcodeFormat::AZTEC));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Ref<Result> AztecReader::decode(Ref<BinaryBitmap> image, DecodeHints) {
|
||||
//cout << "decoding with hints not supported for aztec" << "\n" << flush;
|
||||
return this->decode(image);
|
||||
}
|
||||
|
||||
AztecReader::~AztecReader() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
Decoder& AztecReader::getDecoder() {
|
||||
return decoder_;
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Ref<Result> AztecReader::decode(Ref<BinaryBitmap> image, DecodeHints) {
|
||||
//cout << "decoding with hints not supported for aztec" << "\n" << flush;
|
||||
return this->decode(image);
|
||||
}
|
||||
|
||||
AztecReader::~AztecReader() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
zxing::aztec::Decoder& AztecReader::getDecoder() {
|
||||
return decoder_;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ using zxing::Ref;
|
|||
using std::string;
|
||||
|
||||
namespace {
|
||||
void add(string& result, unsigned char character) {
|
||||
void add(string& result, char character) {
|
||||
#ifndef NO_ICONV
|
||||
char s[] = { character & 0xff };
|
||||
char* ss = s;
|
||||
|
@ -163,9 +163,9 @@ Ref<DecoderResult> Decoder::decode(Ref<zxing::aztec::AztecDetectorResult> detect
|
|||
Ref<String> result = getEncodedData(aCorrectedBits);
|
||||
|
||||
// std::printf("constructing array\n");
|
||||
ArrayRef<unsigned char> arrayOut(aCorrectedBits->getSize());
|
||||
ArrayRef<char> arrayOut(aCorrectedBits->getSize());
|
||||
for (int i = 0; i < aCorrectedBits->count(); i++) {
|
||||
arrayOut[i] = (unsigned char)aCorrectedBits->get(i);
|
||||
arrayOut[i] = (char)aCorrectedBits->get(i);
|
||||
}
|
||||
|
||||
// std::printf("returning\n");
|
||||
|
@ -376,7 +376,7 @@ Ref<BitArray> Decoder::correctBits(Ref<zxing::BitArray> rawbits) {
|
|||
|
||||
}
|
||||
|
||||
flag = (unsigned int)flag >> 1;
|
||||
flag = ((unsigned int)flag) >> 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,10 +29,12 @@
|
|||
#include <zxing/common/detector/math_utils.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
|
||||
using std::vector;
|
||||
using zxing::aztec::Detector;
|
||||
using zxing::aztec::Point;
|
||||
using zxing::aztec::AztecDetectorResult;
|
||||
using zxing::Ref;
|
||||
using zxing::ArrayRef;
|
||||
using zxing::ResultPoint;
|
||||
using zxing::BitArray;
|
||||
using zxing::BitMatrix;
|
||||
|
@ -52,10 +54,15 @@ Ref<AztecDetectorResult> Detector::detect() {
|
|||
std::vector<Ref<Point> > bullEyeCornerPoints = getBullEyeCornerPoints(pCenter);
|
||||
|
||||
extractParameters(bullEyeCornerPoints);
|
||||
|
||||
ArrayRef< Ref<ResultPoint> > corners = getMatrixCornerPoints(bullEyeCornerPoints);
|
||||
|
||||
std::vector<Ref<ResultPoint> > corners = getMatrixCornerPoints(bullEyeCornerPoints);
|
||||
|
||||
Ref<BitMatrix> bits = sampleGrid(image_, corners[shift_%4], corners[(shift_+3)%4], corners[(shift_+2)%4], corners[(shift_+1)%4]);
|
||||
Ref<BitMatrix> bits =
|
||||
sampleGrid(image_,
|
||||
corners[shift_%4],
|
||||
corners[(shift_+3)%4],
|
||||
corners[(shift_+2)%4],
|
||||
corners[(shift_+1)%4]);
|
||||
|
||||
// std::printf("------------\ndetected: compact:%s, nbDataBlocks:%d, nbLayers:%d\n------------\n",compact_?"YES":"NO", nbDataBlocks_, nbLayers_);
|
||||
|
||||
|
@ -127,7 +134,8 @@ void Detector::extractParameters(std::vector<Ref<Point> > bullEyeCornerPoints) {
|
|||
getParameters(parameterData);
|
||||
}
|
||||
|
||||
std::vector<Ref<ResultPoint> > Detector::getMatrixCornerPoints(std::vector<Ref<Point> > bullEyeCornerPoints) {
|
||||
ArrayRef< Ref<ResultPoint> >
|
||||
Detector::getMatrixCornerPoints(std::vector<Ref<Point> > bullEyeCornerPoints) {
|
||||
float ratio = (2 * nbLayers_ + (nbLayers_ > 4 ? 1 : 0) + (nbLayers_ - 4) / 8) / (2.0f * nbCenterLayers_);
|
||||
|
||||
int dx = bullEyeCornerPoints[0]->x - bullEyeCornerPoints[2]->x;
|
||||
|
@ -157,14 +165,13 @@ std::vector<Ref<ResultPoint> > Detector::getMatrixCornerPoints(std::vector<Ref<P
|
|||
!isValid(targetdx, targetdy)) {
|
||||
throw ReaderException("matrix extends over image bounds");
|
||||
}
|
||||
std::vector<Ref<ResultPoint> > returnValue;
|
||||
Array< Ref<ResultPoint> >* array = new Array< Ref<ResultPoint> >();
|
||||
vector< Ref<ResultPoint> >& returnValue (array->values());
|
||||
returnValue.push_back(Ref<ResultPoint>(new ResultPoint(targetax, targetay)));
|
||||
returnValue.push_back(Ref<ResultPoint>(new ResultPoint(targetbx, targetby)));
|
||||
returnValue.push_back(Ref<ResultPoint>(new ResultPoint(targetcx, targetcy)));
|
||||
returnValue.push_back(Ref<ResultPoint>(new ResultPoint(targetdx, targetdy)));
|
||||
|
||||
return returnValue;
|
||||
|
||||
return ArrayRef< Ref<ResultPoint> >(array);
|
||||
}
|
||||
|
||||
void Detector::correctParameterData(Ref<zxing::BitArray> parameterData, bool compact) {
|
||||
|
|
|
@ -29,57 +29,58 @@
|
|||
#include <zxing/aztec/AztecDetectorResult.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace aztec {
|
||||
|
||||
class Point : public Counted {
|
||||
public:
|
||||
int x;
|
||||
int y;
|
||||
|
||||
Ref<ResultPoint> toResultPoint() {
|
||||
return Ref<ResultPoint>(new ResultPoint(x, y));
|
||||
}
|
||||
|
||||
Point(int ax, int ay):x(ax),y(ay) {};
|
||||
|
||||
};
|
||||
|
||||
class Detector : public Counted {
|
||||
|
||||
private:
|
||||
Ref<BitMatrix> image_;
|
||||
|
||||
bool compact_;
|
||||
int nbLayers_;
|
||||
int nbDataBlocks_;
|
||||
int nbCenterLayers_;
|
||||
int shift_;
|
||||
|
||||
void extractParameters(std::vector<Ref<Point> > bullEyeCornerPoints);
|
||||
std::vector<Ref<ResultPoint> > getMatrixCornerPoints(std::vector<Ref<Point> > bullEyeCornerPoints);
|
||||
static void correctParameterData(Ref<BitArray> parameterData, bool compact);
|
||||
std::vector<Ref<Point> > getBullEyeCornerPoints(Ref<Point> pCenter);
|
||||
Ref<Point> getMatrixCenter();
|
||||
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image,
|
||||
Ref<ResultPoint> topLeft,
|
||||
Ref<ResultPoint> bottomLeft,
|
||||
Ref<ResultPoint> bottomRight,
|
||||
Ref<ResultPoint> topRight);
|
||||
void getParameters(Ref<BitArray> parameterData);
|
||||
Ref<BitArray> sampleLine(Ref<Point> p1, Ref<Point> p2, int size);
|
||||
bool isWhiteOrBlackRectangle(Ref<Point> p1,
|
||||
Ref<Point> p2,
|
||||
Ref<Point> p3,
|
||||
Ref<Point> p4);
|
||||
int getColor(Ref<Point> p1, Ref<Point> p2);
|
||||
Ref<Point> getFirstDifferent(Ref<Point> init, bool color, int dx, int dy);
|
||||
bool isValid(int x, int y);
|
||||
static float distance(Ref<Point> a, Ref<Point> b);
|
||||
|
||||
public:
|
||||
Detector(Ref<BitMatrix> image);
|
||||
Ref<AztecDetectorResult> detect();
|
||||
};
|
||||
|
||||
}
|
||||
namespace aztec {
|
||||
class Point;
|
||||
class Detector;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::aztec::Point : public Counted {
|
||||
public:
|
||||
int x;
|
||||
int y;
|
||||
|
||||
Ref<ResultPoint> toResultPoint() {
|
||||
return Ref<ResultPoint>(new ResultPoint(x, y));
|
||||
}
|
||||
|
||||
Point(int ax, int ay):x(ax),y(ay) {};
|
||||
|
||||
};
|
||||
|
||||
class zxing::aztec::Detector : public Counted {
|
||||
|
||||
private:
|
||||
Ref<BitMatrix> image_;
|
||||
|
||||
bool compact_;
|
||||
int nbLayers_;
|
||||
int nbDataBlocks_;
|
||||
int nbCenterLayers_;
|
||||
int shift_;
|
||||
|
||||
void extractParameters(std::vector<Ref<Point> > bullEyeCornerPoints);
|
||||
ArrayRef< Ref<ResultPoint> > getMatrixCornerPoints(std::vector<Ref<Point> > bullEyeCornerPoints);
|
||||
static void correctParameterData(Ref<BitArray> parameterData, bool compact);
|
||||
std::vector<Ref<Point> > getBullEyeCornerPoints(Ref<Point> pCenter);
|
||||
Ref<Point> getMatrixCenter();
|
||||
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image,
|
||||
Ref<ResultPoint> topLeft,
|
||||
Ref<ResultPoint> bottomLeft,
|
||||
Ref<ResultPoint> bottomRight,
|
||||
Ref<ResultPoint> topRight);
|
||||
void getParameters(Ref<BitArray> parameterData);
|
||||
Ref<BitArray> sampleLine(Ref<Point> p1, Ref<Point> p2, int size);
|
||||
bool isWhiteOrBlackRectangle(Ref<Point> p1,
|
||||
Ref<Point> p2,
|
||||
Ref<Point> p3,
|
||||
Ref<Point> p4);
|
||||
int getColor(Ref<Point> p1, Ref<Point> p2);
|
||||
Ref<Point> getFirstDifferent(Ref<Point> init, bool color, int dx, int dy);
|
||||
bool isValid(int x, int y);
|
||||
static float distance(Ref<Point> a, Ref<Point> b);
|
||||
|
||||
public:
|
||||
Detector(Ref<BitMatrix> image);
|
||||
Ref<AztecDetectorResult> detect();
|
||||
};
|
||||
|
|
|
@ -37,13 +37,14 @@ template<typename T> class Array : public Counted {
|
|||
protected:
|
||||
public:
|
||||
std::vector<T> values_;
|
||||
Array(size_t n) :
|
||||
Array() {}
|
||||
Array(int n) :
|
||||
Counted(), values_(n, T()) {
|
||||
}
|
||||
Array(T *ts, size_t n) :
|
||||
Array(T const* ts, int n) :
|
||||
Counted(), values_(ts, ts+n) {
|
||||
}
|
||||
Array(T v, size_t n) :
|
||||
Array(T v, int n) :
|
||||
Counted(), values_(n, v) {
|
||||
}
|
||||
Array(std::vector<T> &v) :
|
||||
|
@ -77,16 +78,19 @@ public:
|
|||
#endif
|
||||
return *this;
|
||||
}
|
||||
T operator[](size_t i) const {
|
||||
T operator[](int i) const {
|
||||
return values_[i];
|
||||
}
|
||||
T& operator[](size_t i) {
|
||||
T& operator[](int i) {
|
||||
return values_[i];
|
||||
}
|
||||
size_t size() const {
|
||||
int size() const {
|
||||
return values_.size();
|
||||
}
|
||||
std::vector<T> values() const {
|
||||
bool empty() const {
|
||||
return values_.size() == 0;
|
||||
}
|
||||
std::vector<T> const& values() const {
|
||||
return values_;
|
||||
}
|
||||
std::vector<T>& values() {
|
||||
|
@ -104,14 +108,14 @@ public:
|
|||
cout << "instantiating empty ArrayRef " << this << "\n";
|
||||
#endif
|
||||
}
|
||||
ArrayRef(size_t n) :
|
||||
explicit ArrayRef(int n) :
|
||||
array_(0) {
|
||||
#ifdef DEBUG_COUNTING
|
||||
cout << "instantiating ArrayRef " << this << "with size " << n << "\n";
|
||||
#endif
|
||||
reset(new Array<T> (n));
|
||||
}
|
||||
ArrayRef(T *ts, size_t n) :
|
||||
ArrayRef(T *ts, int n) :
|
||||
array_(0) {
|
||||
#ifdef DEBUG_COUNTING
|
||||
cout << "instantiating ArrayRef " << this << "with " << n << " elements at " << (void *)ts << "\n";
|
||||
|
@ -160,13 +164,13 @@ public:
|
|||
array_ = 0;
|
||||
}
|
||||
|
||||
T operator[](size_t i) const {
|
||||
T operator[](int i) const {
|
||||
return (*array_)[i];
|
||||
}
|
||||
T& operator[](size_t i) {
|
||||
T& operator[](int i) {
|
||||
return (*array_)[i];
|
||||
}
|
||||
size_t size() const {
|
||||
int size() const {
|
||||
return array_->size();
|
||||
}
|
||||
|
||||
|
@ -201,6 +205,12 @@ public:
|
|||
Array<T>* operator->() {
|
||||
return array_;
|
||||
}
|
||||
operator bool () const {
|
||||
return array_ != 0;
|
||||
}
|
||||
bool operator ! () const {
|
||||
return array_ == 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
|
|
@ -17,28 +17,26 @@
|
|||
|
||||
#include <zxing/common/BitArray.h>
|
||||
|
||||
using namespace std;
|
||||
using std::vector;
|
||||
using zxing::BitArray;
|
||||
|
||||
namespace zxing {
|
||||
|
||||
|
||||
size_t BitArray::wordsForBits(size_t bits) {
|
||||
int BitArray::wordsForBits(int 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(int size) :
|
||||
size_(size), bits_(wordsForBits(size), 0) {
|
||||
}
|
||||
|
||||
BitArray::~BitArray() {
|
||||
}
|
||||
|
||||
size_t BitArray::getSize() {
|
||||
int BitArray::getSize() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
void BitArray::setBulk(size_t i, unsigned int newBits) {
|
||||
void BitArray::setBulk(int i, int newBits) {
|
||||
bits_[i >> logBits_] = newBits;
|
||||
}
|
||||
|
||||
|
@ -69,13 +67,13 @@ void BitArray::setRange(int start, int end) {
|
|||
}
|
||||
|
||||
void BitArray::clear() {
|
||||
size_t max = bits_.size();
|
||||
for (size_t i = 0; i < max; i++) {
|
||||
int max = bits_.size();
|
||||
for (int i = 0; i < max; i++) {
|
||||
bits_[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool BitArray::isRange(size_t start, size_t end, bool value) {
|
||||
bool BitArray::isRange(int start, int end, bool value) {
|
||||
if (end < start) {
|
||||
throw IllegalArgumentException("end must be after start");
|
||||
}
|
||||
|
@ -84,17 +82,17 @@ bool BitArray::isRange(size_t start, size_t end, bool value) {
|
|||
}
|
||||
// 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;
|
||||
int firstWord = start >> logBits_;
|
||||
int lastWord = end >> logBits_;
|
||||
for (int i = firstWord; i <= lastWord; i++) {
|
||||
int firstBit = i > firstWord ? 0 : start & bitsMask_;
|
||||
int lastBit = i < lastWord ? bitsPerWord_ - 1: end & bitsMask_;
|
||||
int mask;
|
||||
if (firstBit == 0 && lastBit == bitsPerWord_ - 1) {
|
||||
mask = numeric_limits<unsigned int>::max();
|
||||
mask = -1;
|
||||
} else {
|
||||
mask = 0;
|
||||
for (size_t j = firstBit; j <= lastBit; j++) {
|
||||
for (int j = firstBit; j <= lastBit; j++) {
|
||||
mask |= 1 << j;
|
||||
}
|
||||
}
|
||||
|
@ -111,17 +109,75 @@ bool BitArray::isRange(size_t start, size_t end, bool value) {
|
|||
return true;
|
||||
}
|
||||
|
||||
vector<unsigned int>& BitArray::getBitArray() {
|
||||
vector<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++) {
|
||||
// std::cerr << "reverse" << std::endl;
|
||||
std::vector<int> newBits(bits_.size(), 0);
|
||||
for (int i = 0; i < size_; i++) {
|
||||
if (get(size_ - i - 1)) {
|
||||
newBits[i >> logBits_] |= 1<< (i & bitsMask_);
|
||||
}
|
||||
}
|
||||
bits_ = newBits;
|
||||
}
|
||||
|
||||
BitArray::Reverse::Reverse(Ref<BitArray> array_) : array(array_) {
|
||||
array->reverse();
|
||||
}
|
||||
|
||||
BitArray::Reverse::~Reverse() {
|
||||
array->reverse();
|
||||
}
|
||||
|
||||
namespace {
|
||||
int numberOfTrailingZeros(int i) {
|
||||
// HD, Figure 5-14
|
||||
int y;
|
||||
if (i == 0) return 32;
|
||||
int n = 31;
|
||||
y = i <<16; if (y != 0) { n = n -16; i = y; }
|
||||
y = i << 8; if (y != 0) { n = n - 8; i = y; }
|
||||
y = i << 4; if (y != 0) { n = n - 4; i = y; }
|
||||
y = i << 2; if (y != 0) { n = n - 2; i = y; }
|
||||
return n - (((unsigned int)(i << 1)) >> 31);
|
||||
}
|
||||
}
|
||||
|
||||
int BitArray::getNextSet(int from) {
|
||||
if (from >= size_) {
|
||||
return size_;
|
||||
}
|
||||
int bitsOffset = from >> 5;
|
||||
int currentBits = bits_[bitsOffset];
|
||||
// mask off lesser bits first
|
||||
currentBits &= ~((1 << (from & 0x1F)) - 1);
|
||||
while (currentBits == 0) {
|
||||
if (++bitsOffset == (int)bits_.size()) {
|
||||
return size_;
|
||||
}
|
||||
currentBits = bits_[bitsOffset];
|
||||
}
|
||||
int result = (bitsOffset << 5) + numberOfTrailingZeros(currentBits);
|
||||
return result > size_ ? size_ : result;
|
||||
}
|
||||
|
||||
int BitArray::getNextUnset(int from) {
|
||||
if (from >= size_) {
|
||||
return size_;
|
||||
}
|
||||
int bitsOffset = from >> 5;
|
||||
int currentBits = ~bits_[bitsOffset];
|
||||
// mask off lesser bits first
|
||||
currentBits &= ~((1 << (from & 0x1F)) - 1);
|
||||
while (currentBits == 0) {
|
||||
if (++bitsOffset == (int)bits_.size()) {
|
||||
return size_;
|
||||
}
|
||||
currentBits = ~bits_[bitsOffset];
|
||||
}
|
||||
int result = (bitsOffset << 5) + numberOfTrailingZeros(currentBits);
|
||||
return result > size_ ? size_ : result;
|
||||
}
|
||||
|
|
|
@ -22,8 +22,12 @@
|
|||
#include <zxing/common/IllegalArgumentException.h>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
|
||||
namespace zxing {
|
||||
class BitArray;
|
||||
std::ostream& operator << (std::ostream&, BitArray const&);
|
||||
}
|
||||
|
||||
#define ZX_LOG_DIGITS(digits) \
|
||||
((digits == 8) ? 3 : \
|
||||
|
@ -33,38 +37,49 @@ namespace zxing {
|
|||
((digits == 128) ? 7 : \
|
||||
(-1))))))
|
||||
|
||||
class BitArray : public Counted {
|
||||
class zxing::BitArray : public Counted {
|
||||
private:
|
||||
size_t size_;
|
||||
std::vector<unsigned int> bits_;
|
||||
static const unsigned int bitsPerWord_ =
|
||||
int size_;
|
||||
std::vector<int> bits_;
|
||||
static const int bitsPerWord_ =
|
||||
std::numeric_limits<unsigned int>::digits;
|
||||
static const unsigned int logBits_ = ZX_LOG_DIGITS(bitsPerWord_);
|
||||
static const unsigned int bitsMask_ = (1 << logBits_) - 1;
|
||||
static size_t wordsForBits(size_t bits);
|
||||
static const int logBits_ = ZX_LOG_DIGITS(bitsPerWord_);
|
||||
static const int bitsMask_ = (1 << logBits_) - 1;
|
||||
static int wordsForBits(int bits);
|
||||
explicit BitArray();
|
||||
|
||||
public:
|
||||
BitArray(size_t size);
|
||||
BitArray(int size);
|
||||
~BitArray();
|
||||
size_t getSize();
|
||||
int getSize() const;
|
||||
|
||||
bool get(size_t i) {
|
||||
bool get(int i) const {
|
||||
return (bits_[i >> logBits_] & (1 << (i & bitsMask_))) != 0;
|
||||
}
|
||||
|
||||
void set(size_t i) {
|
||||
void set(int i) {
|
||||
bits_[i >> logBits_] |= 1 << (i & bitsMask_);
|
||||
}
|
||||
|
||||
void setBulk(size_t i, unsigned int newBits);
|
||||
int getNextSet(int from);
|
||||
int getNextUnset(int from);
|
||||
|
||||
void setBulk(int i, int newBits);
|
||||
void setRange(int start, int end);
|
||||
void clear();
|
||||
bool isRange(size_t start, size_t end, bool value);
|
||||
std::vector<unsigned int>& getBitArray();
|
||||
bool isRange(int start, int end, bool value);
|
||||
std::vector<int>& getBitArray();
|
||||
|
||||
void reverse();
|
||||
class Reverse;
|
||||
};
|
||||
|
||||
}
|
||||
class zxing::BitArray::Reverse {
|
||||
private:
|
||||
Ref<BitArray> array;
|
||||
public:
|
||||
Reverse(Ref<BitArray> array);
|
||||
~Reverse();
|
||||
};
|
||||
|
||||
#endif // __BIT_ARRAY_H__
|
||||
|
|
31
cpp/core/src/zxing/common/BitArrayIO.cpp
Normal file
31
cpp/core/src/zxing/common/BitArrayIO.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
// -*- 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 zxing::BitArray;
|
||||
using std::ostream;
|
||||
|
||||
ostream& zxing::operator << (ostream& os, BitArray const& ba) {
|
||||
for (int i = 0, size = ba.getSize(); i < size; i++) {
|
||||
if ((i & 0x07) == 0) {
|
||||
os << ' ';
|
||||
}
|
||||
os << (ba.get(i) ? 'X' : '.');
|
||||
}
|
||||
return os;
|
||||
}
|
|
@ -27,63 +27,62 @@ using std::ostringstream;
|
|||
|
||||
using zxing::BitMatrix;
|
||||
using zxing::BitArray;
|
||||
using zxing::ArrayRef;
|
||||
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 wordsForSize(int width,
|
||||
int height,
|
||||
int bitsPerWord,
|
||||
int logBits) {
|
||||
int bits = width * height;
|
||||
int arraySize = (bits + bitsPerWord - 1) >> logBits;
|
||||
return arraySize;
|
||||
}
|
||||
}
|
||||
|
||||
BitMatrix::BitMatrix(size_t dimension) :
|
||||
width_(dimension), height_(dimension), words_(0), bits_(NULL) {
|
||||
BitMatrix::BitMatrix(int dimension) :
|
||||
width_(dimension), height_(dimension), words_(0) {
|
||||
words_ = wordsForSize(width_, height_, bitsPerWord, logBits);
|
||||
bits_ = new unsigned int[words_];
|
||||
bits_ = ArrayRef<int>(words_);
|
||||
clear();
|
||||
}
|
||||
|
||||
BitMatrix::BitMatrix(size_t width, size_t height) :
|
||||
width_(width), height_(height), words_(0), bits_(NULL) {
|
||||
BitMatrix::BitMatrix(int width, int height) :
|
||||
width_(width), height_(height), words_(0) {
|
||||
words_ = wordsForSize(width_, height_, bitsPerWord, logBits);
|
||||
bits_ = new unsigned int[words_];
|
||||
bits_ = ArrayRef<int>(words_);
|
||||
clear();
|
||||
}
|
||||
|
||||
BitMatrix::~BitMatrix() {
|
||||
delete[] bits_;
|
||||
}
|
||||
BitMatrix::~BitMatrix() {}
|
||||
|
||||
|
||||
void BitMatrix::flip(size_t x, size_t y) {
|
||||
size_t offset = x + width_ * y;
|
||||
void BitMatrix::flip(int x, int y) {
|
||||
int offset = x + width_ * y;
|
||||
bits_[offset >> logBits] ^= 1 << (offset & bitsMask);
|
||||
}
|
||||
|
||||
void BitMatrix::clear() {
|
||||
std::fill(bits_, bits_+words_, 0);
|
||||
std::fill(&bits_[0], &bits_[words_], 0);
|
||||
}
|
||||
|
||||
void BitMatrix::setRegion(size_t left, size_t top, size_t width, size_t height) {
|
||||
void BitMatrix::setRegion(int left, int top, int width, int 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;
|
||||
int right = left + width;
|
||||
int 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++) {
|
||||
for (int y = top; y < bottom; y++) {
|
||||
int yOffset = width_ * y;
|
||||
for (size_t x = left; x < right; x++) {
|
||||
size_t offset = x + yOffset;
|
||||
for (int x = left; x < right; x++) {
|
||||
int offset = x + yOffset;
|
||||
bits_[offset >> logBits] |= 1 << (offset & bitsMask);
|
||||
}
|
||||
}
|
||||
|
@ -95,26 +94,26 @@ Ref<BitArray> BitMatrix::getRow(int y, Ref<BitArray> row) {
|
|||
} 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;
|
||||
int start = y * width_;
|
||||
int end = start + width_ - 1; // end is inclusive
|
||||
int firstWord = start >> logBits;
|
||||
int lastWord = end >> logBits;
|
||||
int bitOffset = start & bitsMask;
|
||||
for (int i = firstWord; i <= lastWord; i++) {
|
||||
int firstBit = i > firstWord ? 0 : start & bitsMask;
|
||||
int lastBit = i < lastWord ? bitsPerWord - 1 : end & bitsMask;
|
||||
int mask;
|
||||
if (firstBit == 0 && lastBit == logBits) {
|
||||
mask = std::numeric_limits<unsigned int>::max();
|
||||
mask = std::numeric_limits<int>::max();
|
||||
} else {
|
||||
mask = 0;
|
||||
for (size_t j = firstBit; j <= lastBit; j++) {
|
||||
for (int 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];
|
||||
int prevBulk = row->getBitArray()[i - firstWord - 1];
|
||||
prevBulk |= (bits_[i] & mask) << (bitsPerWord - bitOffset);
|
||||
row->setBulk((i - firstWord - 1) << logBits, prevBulk);
|
||||
}
|
||||
|
@ -122,26 +121,26 @@ Ref<BitArray> BitMatrix::getRow(int y, Ref<BitArray> row) {
|
|||
return row;
|
||||
}
|
||||
|
||||
size_t BitMatrix::getWidth() const {
|
||||
int BitMatrix::getWidth() const {
|
||||
return width_;
|
||||
}
|
||||
|
||||
size_t BitMatrix::getHeight() const {
|
||||
int BitMatrix::getHeight() const {
|
||||
return height_;
|
||||
}
|
||||
|
||||
size_t BitMatrix::getDimension() const {
|
||||
int BitMatrix::getDimension() const {
|
||||
return width_;
|
||||
}
|
||||
|
||||
unsigned int* BitMatrix::getBits() const {
|
||||
ArrayRef<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++) {
|
||||
for (int y = 0; y < bm.height_; y++) {
|
||||
for (int x = 0; x < bm.width_; x++) {
|
||||
out << (bm.get(x, y) ? "X " : " ");
|
||||
}
|
||||
out << "\n";
|
||||
|
|
|
@ -23,16 +23,19 @@
|
|||
|
||||
#include <zxing/common/Counted.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <limits>
|
||||
|
||||
namespace zxing {
|
||||
class BitMatrix;
|
||||
}
|
||||
|
||||
class BitMatrix : public Counted {
|
||||
class zxing::BitMatrix : public Counted {
|
||||
private:
|
||||
size_t width_;
|
||||
size_t height_;
|
||||
size_t words_;
|
||||
unsigned int* bits_;
|
||||
int width_;
|
||||
int height_;
|
||||
int words_;
|
||||
ArrayRef<int> bits_;
|
||||
|
||||
#define ZX_LOG_DIGITS(digits) \
|
||||
((digits == 8) ? 3 : \
|
||||
|
@ -42,37 +45,37 @@ private:
|
|||
((digits == 128) ? 7 : \
|
||||
(-1))))))
|
||||
|
||||
static const unsigned int bitsPerWord =
|
||||
static const int bitsPerWord =
|
||||
std::numeric_limits<unsigned int>::digits;
|
||||
static const unsigned int logBits = ZX_LOG_DIGITS(bitsPerWord);
|
||||
static const unsigned int bitsMask = (1 << logBits) - 1;
|
||||
static const int logBits = ZX_LOG_DIGITS(bitsPerWord);
|
||||
static const int bitsMask = (1 << logBits) - 1;
|
||||
|
||||
public:
|
||||
BitMatrix(size_t dimension);
|
||||
BitMatrix(size_t width, size_t height);
|
||||
BitMatrix(int dimension);
|
||||
BitMatrix(int width, int height);
|
||||
|
||||
~BitMatrix();
|
||||
|
||||
bool get(size_t x, size_t y) const {
|
||||
size_t offset = x + width_ * y;
|
||||
bool get(int x, int y) const {
|
||||
int offset = x + width_ * y;
|
||||
return ((bits_[offset >> logBits] >> (offset & bitsMask)) & 0x01) != 0;
|
||||
}
|
||||
|
||||
void set(size_t x, size_t y) {
|
||||
size_t offset = x + width_ * y;
|
||||
void set(int x, int y) {
|
||||
int offset = x + width_ * y;
|
||||
bits_[offset >> logBits] |= 1 << (offset & bitsMask);
|
||||
}
|
||||
|
||||
void flip(size_t x, size_t y);
|
||||
void flip(int x, int y);
|
||||
void clear();
|
||||
void setRegion(size_t left, size_t top, size_t width, size_t height);
|
||||
void setRegion(int left, int top, int width, int height);
|
||||
Ref<BitArray> getRow(int y, Ref<BitArray> row);
|
||||
|
||||
size_t getDimension() const;
|
||||
size_t getWidth() const;
|
||||
size_t getHeight() const;
|
||||
int getDimension() const;
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
unsigned int* getBits() const;
|
||||
ArrayRef<int> getBits() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream &out, const BitMatrix &bm);
|
||||
const char *description();
|
||||
|
@ -82,6 +85,4 @@ private:
|
|||
BitMatrix& operator =(const BitMatrix&);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __BIT_MATRIX_H__
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace zxing {
|
|||
* @author christian.brunschen@gmail.com (Christian Brunschen)
|
||||
*/
|
||||
class BitSource : public Counted {
|
||||
typedef unsigned char byte;
|
||||
typedef char byte;
|
||||
private:
|
||||
ArrayRef<byte> bytes_;
|
||||
int byteOffset_;
|
||||
|
|
|
@ -97,6 +97,7 @@ public:
|
|||
reset(o);
|
||||
}
|
||||
|
||||
/*
|
||||
explicit Ref(const T &o) :
|
||||
object_(0) {
|
||||
#ifdef DEBUG_COUNTING
|
||||
|
@ -104,6 +105,7 @@ public:
|
|||
#endif
|
||||
reset(const_cast<T *>(&o));
|
||||
}
|
||||
*/
|
||||
|
||||
Ref(const Ref &other) :
|
||||
object_(0) {
|
||||
|
|
|
@ -24,20 +24,20 @@
|
|||
using namespace std;
|
||||
using namespace zxing;
|
||||
|
||||
DecoderResult::DecoderResult(ArrayRef<unsigned char> rawBytes,
|
||||
DecoderResult::DecoderResult(ArrayRef<char> rawBytes,
|
||||
Ref<String> text,
|
||||
ArrayRef< ArrayRef<unsigned char> >& byteSegments,
|
||||
ArrayRef< ArrayRef<char> >& byteSegments,
|
||||
string const& ecLevel) :
|
||||
rawBytes_(rawBytes),
|
||||
text_(text),
|
||||
byteSegments_(byteSegments),
|
||||
ecLevel_(ecLevel) {}
|
||||
|
||||
DecoderResult::DecoderResult(ArrayRef<unsigned char> rawBytes,
|
||||
DecoderResult::DecoderResult(ArrayRef<char> rawBytes,
|
||||
Ref<String> text)
|
||||
: rawBytes_(rawBytes), text_(text) {}
|
||||
|
||||
ArrayRef<unsigned char> DecoderResult::getRawBytes() {
|
||||
ArrayRef<char> DecoderResult::getRawBytes() {
|
||||
return rawBytes_;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,20 +29,20 @@ namespace zxing {
|
|||
|
||||
class DecoderResult : public Counted {
|
||||
private:
|
||||
ArrayRef<unsigned char> rawBytes_;
|
||||
ArrayRef<char> rawBytes_;
|
||||
Ref<String> text_;
|
||||
ArrayRef< ArrayRef<unsigned char> > byteSegments_;
|
||||
ArrayRef< ArrayRef<char> > byteSegments_;
|
||||
std::string ecLevel_;
|
||||
|
||||
public:
|
||||
DecoderResult(ArrayRef<unsigned char> rawBytes,
|
||||
DecoderResult(ArrayRef<char> rawBytes,
|
||||
Ref<String> text,
|
||||
ArrayRef< ArrayRef<unsigned char> >& byteSegments,
|
||||
ArrayRef< ArrayRef<char> >& byteSegments,
|
||||
std::string const& ecLevel);
|
||||
|
||||
DecoderResult(ArrayRef<unsigned char> rawBytes, Ref<String> text);
|
||||
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text);
|
||||
|
||||
ArrayRef<unsigned char> getRawBytes();
|
||||
ArrayRef<char> getRawBytes();
|
||||
Ref<String> getText();
|
||||
};
|
||||
|
||||
|
|
|
@ -23,15 +23,16 @@
|
|||
|
||||
namespace zxing {
|
||||
|
||||
DetectorResult::DetectorResult(Ref<BitMatrix> bits, std::vector<Ref<ResultPoint> > points) :
|
||||
bits_(bits), points_(points) {
|
||||
DetectorResult::DetectorResult(Ref<BitMatrix> bits,
|
||||
ArrayRef< Ref<ResultPoint> > points)
|
||||
: bits_(bits), points_(points) {
|
||||
}
|
||||
|
||||
Ref<BitMatrix> DetectorResult::getBits() {
|
||||
return bits_;
|
||||
}
|
||||
|
||||
std::vector<Ref<ResultPoint> > DetectorResult::getPoints() {
|
||||
ArrayRef< Ref<ResultPoint> > DetectorResult::getPoints() {
|
||||
return points_;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,24 +20,24 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <zxing/common/Counted.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/common/BitMatrix.h>
|
||||
#include <zxing/ResultPoint.h>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class DetectorResult : public Counted {
|
||||
private:
|
||||
Ref<BitMatrix> bits_;
|
||||
std::vector<Ref<ResultPoint> > points_;
|
||||
|
||||
public:
|
||||
DetectorResult(Ref<BitMatrix> bits, std::vector<Ref<ResultPoint> > points);
|
||||
Ref<BitMatrix> getBits();
|
||||
std::vector<Ref<ResultPoint> > getPoints();
|
||||
};
|
||||
class DetectorResult;
|
||||
}
|
||||
|
||||
class zxing::DetectorResult : public Counted {
|
||||
private:
|
||||
Ref<BitMatrix> bits_;
|
||||
ArrayRef< Ref<ResultPoint> > points_;
|
||||
|
||||
public:
|
||||
DetectorResult(Ref<BitMatrix> bits, ArrayRef< Ref<ResultPoint> > points);
|
||||
Ref<BitMatrix> getBits();
|
||||
ArrayRef< Ref<ResultPoint> > getPoints();
|
||||
};
|
||||
|
||||
#endif // __DETECTOR_RESULT_H__
|
||||
|
|
|
@ -19,35 +19,39 @@
|
|||
*/
|
||||
|
||||
#include <zxing/common/GlobalHistogramBinarizer.h>
|
||||
#include <zxing/common/IllegalArgumentException.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
#include <zxing/common/Array.h>
|
||||
|
||||
namespace zxing {
|
||||
using namespace std;
|
||||
|
||||
const int LUMINANCE_BITS = 5;
|
||||
const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
|
||||
const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
|
||||
|
||||
GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source) :
|
||||
Binarizer(source), cached_matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) {
|
||||
using zxing::GlobalHistogramBinarizer;
|
||||
using zxing::Binarizer;
|
||||
using zxing::ArrayRef;
|
||||
using zxing::Ref;
|
||||
using zxing::BitArray;
|
||||
using zxing::BitMatrix;
|
||||
|
||||
namespace {
|
||||
const int LUMINANCE_BITS = 5;
|
||||
const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
|
||||
const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
|
||||
const ArrayRef<char> EMPTY (0);
|
||||
}
|
||||
|
||||
GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {
|
||||
}
|
||||
GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source)
|
||||
: Binarizer(source), luminances(EMPTY), buckets(LUMINANCE_BUCKETS) {}
|
||||
|
||||
GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {}
|
||||
|
||||
void GlobalHistogramBinarizer::initArrays(int luminanceSize) {
|
||||
if (luminances.size() < luminanceSize) {
|
||||
luminances = ArrayRef<char>(luminanceSize);
|
||||
}
|
||||
for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
|
||||
buckets[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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, 0);
|
||||
// std::cerr << "gbr " << y << std::endl;
|
||||
LuminanceSource& source = *getLuminanceSource();
|
||||
int width = source.getWidth();
|
||||
if (row == NULL || static_cast<int>(row->getSize()) < width) {
|
||||
|
@ -56,99 +60,96 @@ Ref<BitArray> GlobalHistogramBinarizer::getBlackRow(int y, Ref<BitArray> row) {
|
|||
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]++;
|
||||
initArrays(width);
|
||||
ArrayRef<char> localLuminances = source.getRow(y, luminances);
|
||||
if (false) {
|
||||
std::cerr << "gbr " << y << " r ";
|
||||
for(int i=0, e=localLuminances->size(); i < e; ++i) {
|
||||
std::cerr << 0+localLuminances[i] << " ";
|
||||
}
|
||||
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;
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
ArrayRef<int> localBuckets = buckets;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pixel = localLuminances[x] & 0xff;
|
||||
localBuckets[pixel >> LUMINANCE_SHIFT]++;
|
||||
}
|
||||
int blackPoint = estimateBlackPoint(localBuckets);
|
||||
// std::cerr << "gbr bp " << y << " " << blackPoint << std::endl;
|
||||
|
||||
int left = localLuminances[0] & 0xff;
|
||||
int center = localLuminances[1] & 0xff;
|
||||
for (int x = 1; x < width - 1; x++) {
|
||||
int right = localLuminances[x + 1] & 0xff;
|
||||
// A simple -1 4 -1 box filter with a weight of 2.
|
||||
int luminance = ((center << 2) - left - right) >> 1;
|
||||
if (luminance < blackPoint) {
|
||||
row->set(x);
|
||||
}
|
||||
left = center;
|
||||
center = right;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
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, 0);
|
||||
Ref<BitMatrix> matrix(new BitMatrix(width, height));
|
||||
|
||||
// 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];
|
||||
initArrays(width);
|
||||
ArrayRef<int> localBuckets = buckets;
|
||||
for (int y = 1; y < 5; y++) {
|
||||
int rownum = height * y / 5;
|
||||
int row = height * y / 5;
|
||||
ArrayRef<char> localLuminances = source.getRow(row, luminances);
|
||||
int right = (width << 2) / 5;
|
||||
row = source.getRow(rownum, row);
|
||||
for (int x = width / 5; x < right; x++) {
|
||||
histogram[row[x] >> LUMINANCE_SHIFT]++;
|
||||
int pixel = localLuminances[x] & 0xff;
|
||||
localBuckets[pixel >> LUMINANCE_SHIFT]++;
|
||||
}
|
||||
}
|
||||
|
||||
int blackPoint = estimate(histogram);
|
||||
int blackPoint = estimateBlackPoint(localBuckets);
|
||||
|
||||
Ref<BitMatrix> matrix_ref(new BitMatrix(width, height));
|
||||
BitMatrix& matrix = *matrix_ref;
|
||||
ArrayRef<char> localLuminances = source.getMatrix();
|
||||
for (int y = 0; y < height; y++) {
|
||||
row = source.getRow(y, row);
|
||||
int offset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (row[x] < blackPoint)
|
||||
matrix.set(x, y);
|
||||
int pixel = localLuminances[offset + x] & 0xff;
|
||||
if (pixel < blackPoint) {
|
||||
matrix->set(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cached_matrix_ = matrix_ref;
|
||||
// delete [] row;
|
||||
return matrix_ref;
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
int GlobalHistogramBinarizer::estimate(vector<int> &histogram) {
|
||||
int numBuckets = histogram.size();
|
||||
int maxBucketCount = 0;
|
||||
using namespace std;
|
||||
|
||||
int GlobalHistogramBinarizer::estimateBlackPoint(ArrayRef<int> const& buckets) {
|
||||
// Find tallest peak in histogram
|
||||
int numBuckets = buckets.size();
|
||||
int maxBucketCount = 0;
|
||||
int firstPeak = 0;
|
||||
int firstPeakSize = 0;
|
||||
for (int i = 0; i < numBuckets; i++) {
|
||||
if (histogram[i] > firstPeakSize) {
|
||||
firstPeak = i;
|
||||
firstPeakSize = histogram[i];
|
||||
if (false) {
|
||||
for (int x = 0; x < numBuckets; x++) {
|
||||
cerr << buckets[x] << " ";
|
||||
}
|
||||
if (histogram[i] > maxBucketCount) {
|
||||
maxBucketCount = histogram[i];
|
||||
cerr << endl;
|
||||
}
|
||||
for (int x = 0; x < numBuckets; x++) {
|
||||
if (buckets[x] > firstPeakSize) {
|
||||
firstPeak = x;
|
||||
firstPeakSize = buckets[x];
|
||||
}
|
||||
if (buckets[x] > maxBucketCount) {
|
||||
maxBucketCount = buckets[x];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,17 +157,16 @@ int GlobalHistogramBinarizer::estimate(vector<int> &histogram) {
|
|||
// so close to the first one
|
||||
int secondPeak = 0;
|
||||
int secondPeakScore = 0;
|
||||
for (int i = 0; i < numBuckets; i++) {
|
||||
int distanceToBiggest = i - firstPeak;
|
||||
for (int x = 0; x < numBuckets; x++) {
|
||||
int distanceToBiggest = x - firstPeak;
|
||||
// Encourage more distant second peaks by multiplying by square of distance
|
||||
int score = histogram[i] * distanceToBiggest * distanceToBiggest;
|
||||
int score = buckets[x] * distanceToBiggest * distanceToBiggest;
|
||||
if (score > secondPeakScore) {
|
||||
secondPeak = i;
|
||||
secondPeak = x;
|
||||
secondPeakScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
// Put firstPeak first
|
||||
if (firstPeak > secondPeak) {
|
||||
int temp = firstPeak;
|
||||
firstPeak = secondPeak;
|
||||
|
@ -180,30 +180,30 @@ int GlobalHistogramBinarizer::estimate(vector<int> &histogram) {
|
|||
// a false positive for 1D formats, which are relatively lenient.
|
||||
// We arbitrarily say "close" is
|
||||
// "<= 1/16 of the total histogram buckets apart"
|
||||
// std::cerr << "! " << secondPeak << " " << firstPeak << " " << numBuckets << std::endl;
|
||||
if (secondPeak - firstPeak <= numBuckets >> 4) {
|
||||
throw IllegalArgumentException("Too little dynamic range in luminance");
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
||||
// 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;
|
||||
for (int x = secondPeak - 1; x > firstPeak; x--) {
|
||||
int fromFirst = x - 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]);
|
||||
int score = fromFirst * fromFirst * (secondPeak - x) *
|
||||
(maxBucketCount - buckets[x]);
|
||||
if (score > bestValleyScore) {
|
||||
bestValley = i;
|
||||
bestValley = x;
|
||||
bestValleyScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
// std::cerr << "bps " << (bestValley << LUMINANCE_SHIFT) << std::endl;
|
||||
return bestValley << LUMINANCE_SHIFT;
|
||||
}
|
||||
|
||||
Ref<Binarizer> GlobalHistogramBinarizer::createBinarizer(Ref<LuminanceSource> source) {
|
||||
return Ref<Binarizer> (new GlobalHistogramBinarizer(source));
|
||||
}
|
||||
|
||||
} // namespace zxing
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __GLOBALHISTOGRAMBINARIZER_H__
|
||||
#define __GLOBALHISTOGRAMBINARIZER_H__
|
||||
/*
|
||||
|
@ -19,29 +20,29 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <zxing/Binarizer.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/common/BitMatrix.h>
|
||||
#include <zxing/common/Array.h>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class GlobalHistogramBinarizer : public Binarizer {
|
||||
private:
|
||||
Ref<BitMatrix> cached_matrix_;
|
||||
Ref<BitArray> cached_row_;
|
||||
int cached_row_num_;
|
||||
|
||||
public:
|
||||
GlobalHistogramBinarizer(Ref<LuminanceSource> source);
|
||||
virtual ~GlobalHistogramBinarizer();
|
||||
|
||||
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row);
|
||||
virtual Ref<BitMatrix> getBlackMatrix();
|
||||
static int estimate(std::vector<int> &histogram);
|
||||
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source);
|
||||
};
|
||||
|
||||
class GlobalHistogramBinarizer;
|
||||
}
|
||||
|
||||
|
||||
class zxing::GlobalHistogramBinarizer : public Binarizer {
|
||||
private:
|
||||
ArrayRef<char> luminances;
|
||||
ArrayRef<int> buckets;
|
||||
public:
|
||||
GlobalHistogramBinarizer(Ref<LuminanceSource> source);
|
||||
virtual ~GlobalHistogramBinarizer();
|
||||
|
||||
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row);
|
||||
virtual Ref<BitMatrix> getBlackMatrix();
|
||||
static int estimateBlackPoint(ArrayRef<int> const& buckets);
|
||||
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source);
|
||||
private:
|
||||
void initArrays(int luminanceSize);
|
||||
};
|
||||
|
||||
#endif /* GLOBALHISTOGRAMBINARIZER_H_ */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* GreyscaleLuminanceSource.cpp
|
||||
* zxing
|
||||
|
@ -21,50 +22,90 @@
|
|||
#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) {
|
||||
using zxing::Ref;
|
||||
using zxing::ArrayRef;
|
||||
using zxing::LuminanceSource;
|
||||
using zxing::GreyscaleLuminanceSource;
|
||||
|
||||
GreyscaleLuminanceSource::
|
||||
GreyscaleLuminanceSource(ArrayRef<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) {
|
||||
ArrayRef<char> GreyscaleLuminanceSource::getRow(int y, ArrayRef<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_];
|
||||
if (!row || row.size() < width) {
|
||||
ArrayRef<char> temp (width);
|
||||
row = temp;
|
||||
}
|
||||
int offset = (y + top_) * dataWidth_ + left_;
|
||||
memcpy(row, &greyData_[offset], width);
|
||||
memcpy(&row[0], &greyData_[offset], width);
|
||||
return row;
|
||||
}
|
||||
|
||||
unsigned char* GreyscaleLuminanceSource::getMatrix() {
|
||||
ArrayRef<char> GreyscaleLuminanceSource::getMatrix() {
|
||||
int size = width_ * height_;
|
||||
unsigned char* result = new unsigned char[size];
|
||||
ArrayRef<char> result (size);
|
||||
if (left_ == 0 && top_ == 0 && dataWidth_ == width_ && dataHeight_ == height_) {
|
||||
memcpy(result, greyData_, size);
|
||||
memcpy(&result[0], &greyData_[0], size);
|
||||
} else {
|
||||
for (int row = 0; row < height_; row++) {
|
||||
memcpy(result + row * width_, greyData_ + (top_ + row) * dataWidth_ + left_, width_);
|
||||
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_));
|
||||
}
|
||||
extern "C" void abort();
|
||||
|
||||
} /* namespace */
|
||||
Ref<LuminanceSource> GreyscaleLuminanceSource::rotateCounterClockwise() {
|
||||
// Intentionally flip the left, top, width, and height arguments as
|
||||
// needed. dataWidth and dataHeight are always kept unrotated.
|
||||
Ref<LuminanceSource> result (
|
||||
new GreyscaleRotatedLuminanceSource(greyData_,
|
||||
dataWidth_, dataHeight_,
|
||||
top_, left_, height_, width_)
|
||||
);
|
||||
|
||||
/*
|
||||
// testing code ... remove when we trust everything.
|
||||
// NB: very slow, so don't forget and leave on.
|
||||
for(int r = top_; r < top_+height_; ++r) {
|
||||
for(int c = left_; c < left_+width_; ++c) {
|
||||
int rot_r = r;
|
||||
int rot_c = c;
|
||||
// transpose
|
||||
int tmp = rot_r;
|
||||
rot_r = rot_c;
|
||||
rot_c = tmp;
|
||||
// flip vert
|
||||
rot_r = width_-rot_r-1;
|
||||
if (getRow(r, ArrayRef<char>())[c] !=
|
||||
result->getRow(rot_r, ArrayRef<char>())[rot_c]) {
|
||||
using namespace std;
|
||||
cerr << r << "," << c << " "
|
||||
<< rot_r << "," << rot_c << " "
|
||||
<< (0+getRow(r, ArrayRef<char>())[c]) << " "
|
||||
<< (0+result->getRow(rot_r, ArrayRef<char>())[rot_c]) << " "
|
||||
<< endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __GREYSCALE_LUMINANCE_SOURCE__
|
||||
#define __GREYSCALE_LUMINANCE_SOURCE__
|
||||
/*
|
||||
|
@ -22,11 +23,13 @@
|
|||
#include <zxing/LuminanceSource.h>
|
||||
|
||||
namespace zxing {
|
||||
class GreyscaleLuminanceSource;
|
||||
}
|
||||
|
||||
class GreyscaleLuminanceSource : public LuminanceSource {
|
||||
class zxing::GreyscaleLuminanceSource : public LuminanceSource {
|
||||
|
||||
private:
|
||||
unsigned char* greyData_;
|
||||
private:
|
||||
ArrayRef<char> greyData_;
|
||||
int dataWidth_;
|
||||
int dataHeight_;
|
||||
int left_;
|
||||
|
@ -34,12 +37,12 @@ class GreyscaleLuminanceSource : public LuminanceSource {
|
|||
int width_;
|
||||
int height_;
|
||||
|
||||
public:
|
||||
GreyscaleLuminanceSource(unsigned char* greyData, int dataWidth, int dataHeight, int left,
|
||||
int top, int width, int height);
|
||||
public:
|
||||
GreyscaleLuminanceSource(ArrayRef<char> greyData, int dataWidth, int dataHeight, int left,
|
||||
int top, int width, int height);
|
||||
|
||||
unsigned char* getRow(int y, unsigned char* row);
|
||||
unsigned char* getMatrix();
|
||||
ArrayRef<char> getRow(int y, ArrayRef<char> row);
|
||||
ArrayRef<char> getMatrix();
|
||||
|
||||
bool isRotateSupported() const {
|
||||
return true;
|
||||
|
@ -57,6 +60,4 @@ class GreyscaleLuminanceSource : public LuminanceSource {
|
|||
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* GreyscaleRotatedLuminanceSource.cpp
|
||||
* zxing
|
||||
|
@ -21,15 +22,21 @@
|
|||
#include <zxing/common/GreyscaleRotatedLuminanceSource.h>
|
||||
#include <zxing/common/IllegalArgumentException.h>
|
||||
|
||||
namespace zxing {
|
||||
using zxing::ArrayRef;
|
||||
using zxing::GreyscaleRotatedLuminanceSource;
|
||||
|
||||
// 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), left_(left), top_(top), width_(width),
|
||||
// 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(ArrayRef<char> greyData,
|
||||
int dataWidth, int /* dataHeight */,
|
||||
int left, int top,
|
||||
int width, int height)
|
||||
: greyData_(greyData),
|
||||
dataWidth_(dataWidth),
|
||||
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.");
|
||||
|
@ -37,29 +44,38 @@ GreyscaleRotatedLuminanceSource::GreyscaleRotatedLuminanceSource(unsigned char*
|
|||
}
|
||||
|
||||
// The API asks for rows, but we're rotated, so we return columns.
|
||||
unsigned char* GreyscaleRotatedLuminanceSource::getRow(int y, unsigned char* row) {
|
||||
ArrayRef<char>
|
||||
GreyscaleRotatedLuminanceSource::getRow(int y, ArrayRef<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];
|
||||
if (!row || row.size() < width_) {
|
||||
row = ArrayRef<char>(width_);
|
||||
}
|
||||
int offset = (left_ * dataWidth_) + (dataWidth_ - 1 - (y + top_));
|
||||
for (int x = 0; x < width; x++) {
|
||||
using namespace std;
|
||||
if (false) {
|
||||
cerr << offset << " = "
|
||||
<< top_ << " " << left_ << " "
|
||||
<< height_ << " " << width_ << " "
|
||||
<< y << endl;
|
||||
}
|
||||
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.
|
||||
ArrayRef<char> GreyscaleRotatedLuminanceSource::getMatrix() {
|
||||
ArrayRef<char> result (width_ * height_);
|
||||
for (int y = 0; y < height_; y++) {
|
||||
getRow(y, &result[y * width_]);
|
||||
char* row = &result[y * width_];
|
||||
int offset = (left_ * dataWidth_) + (dataWidth_ - 1 - (y + top_));
|
||||
for (int x = 0; x < width_; x++) {
|
||||
row[x] = greyData_[offset];
|
||||
offset += dataWidth_;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __GREYSCALE_ROTATED_LUMINANCE_SOURCE__
|
||||
#define __GREYSCALE_ROTATED_LUMINANCE_SOURCE__
|
||||
/*
|
||||
|
@ -23,22 +24,24 @@
|
|||
#include <zxing/LuminanceSource.h>
|
||||
|
||||
namespace zxing {
|
||||
class GreyscaleRotatedLuminanceSource;
|
||||
}
|
||||
|
||||
class GreyscaleRotatedLuminanceSource : public LuminanceSource {
|
||||
class zxing::GreyscaleRotatedLuminanceSource : public LuminanceSource {
|
||||
private:
|
||||
unsigned char* greyData_;
|
||||
int dataWidth_;
|
||||
int left_;
|
||||
int top_;
|
||||
int width_;
|
||||
int height_;
|
||||
ArrayRef<char> greyData_;
|
||||
const int dataWidth_;
|
||||
const int left_;
|
||||
const int top_;
|
||||
const int width_;
|
||||
const int height_;
|
||||
|
||||
public:
|
||||
GreyscaleRotatedLuminanceSource(unsigned char* greyData, int dataWidth, int dataHeight,
|
||||
GreyscaleRotatedLuminanceSource(ArrayRef<char> greyData, int dataWidth, int dataHeight,
|
||||
int left, int top, int width, int height);
|
||||
|
||||
unsigned char* getRow(int y, unsigned char* row);
|
||||
unsigned char* getMatrix();
|
||||
ArrayRef<char> getRow(int y, ArrayRef<char> row);
|
||||
ArrayRef<char> getMatrix();
|
||||
|
||||
bool isRotateSupported() const {
|
||||
return false;
|
||||
|
@ -54,6 +57,4 @@ public:
|
|||
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -59,7 +59,7 @@ Ref<BitMatrix> HybridBinarizer::getBlackMatrix() {
|
|||
int width = source.getWidth();
|
||||
int height = source.getHeight();
|
||||
if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION) {
|
||||
unsigned char* luminances = source.getMatrix();
|
||||
ArrayRef<char> luminances = source.getMatrix();
|
||||
int subWidth = width >> BLOCK_SIZE_POWER;
|
||||
if ((width & BLOCK_SIZE_MASK) != 0) {
|
||||
subWidth++;
|
||||
|
@ -68,7 +68,7 @@ Ref<BitMatrix> HybridBinarizer::getBlackMatrix() {
|
|||
if ((height & BLOCK_SIZE_MASK) != 0) {
|
||||
subHeight++;
|
||||
}
|
||||
int* blackPoints =
|
||||
ArrayRef<int> blackPoints =
|
||||
calculateBlackPoints(luminances, subWidth, subHeight, width, height);
|
||||
|
||||
Ref<BitMatrix> newMatrix (new BitMatrix(width, height));
|
||||
|
@ -80,13 +80,6 @@ Ref<BitMatrix> HybridBinarizer::getBlackMatrix() {
|
|||
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();
|
||||
|
@ -101,12 +94,12 @@ namespace {
|
|||
}
|
||||
|
||||
void
|
||||
HybridBinarizer::calculateThresholdForBlock(unsigned char* luminances,
|
||||
HybridBinarizer::calculateThresholdForBlock(ArrayRef<char> luminances,
|
||||
int subWidth,
|
||||
int subHeight,
|
||||
int width,
|
||||
int height,
|
||||
int blackPoints[],
|
||||
ArrayRef<int> blackPoints,
|
||||
Ref<BitMatrix> const& matrix) {
|
||||
for (int y = 0; y < subHeight; y++) {
|
||||
int yoffset = y << BLOCK_SIZE_POWER;
|
||||
|
@ -137,7 +130,7 @@ HybridBinarizer::calculateThresholdForBlock(unsigned char* luminances,
|
|||
}
|
||||
}
|
||||
|
||||
void HybridBinarizer::thresholdBlock(unsigned char* luminances,
|
||||
void HybridBinarizer::thresholdBlock(ArrayRef<char> luminances,
|
||||
int xoffset,
|
||||
int yoffset,
|
||||
int threshold,
|
||||
|
@ -156,7 +149,7 @@ void HybridBinarizer::thresholdBlock(unsigned char* luminances,
|
|||
}
|
||||
|
||||
namespace {
|
||||
inline int getBlackPointFromNeighbors(int* blackPoints, int subWidth, int x, int y) {
|
||||
inline int getBlackPointFromNeighbors(ArrayRef<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;
|
||||
|
@ -164,14 +157,14 @@ namespace {
|
|||
}
|
||||
|
||||
|
||||
int* HybridBinarizer::calculateBlackPoints(unsigned char* luminances,
|
||||
int subWidth,
|
||||
int subHeight,
|
||||
int width,
|
||||
int height) {
|
||||
ArrayRef<int> HybridBinarizer::calculateBlackPoints(ArrayRef<char> luminances,
|
||||
int subWidth,
|
||||
int subHeight,
|
||||
int width,
|
||||
int height) {
|
||||
const int minDynamicRange = 24;
|
||||
|
||||
int *blackPoints = new int[subHeight * subWidth];
|
||||
ArrayRef<int> blackPoints (subHeight * subWidth);
|
||||
for (int y = 0; y < subHeight; y++) {
|
||||
int yoffset = y << BLOCK_SIZE_POWER;
|
||||
int maxYOffset = height - BLOCK_SIZE;
|
||||
|
|
|
@ -42,19 +42,19 @@ namespace zxing {
|
|||
private:
|
||||
// We'll be using one-D arrays because C++ can't dynamically allocate 2D
|
||||
// arrays
|
||||
int* calculateBlackPoints(unsigned char* luminances,
|
||||
int subWidth,
|
||||
int subHeight,
|
||||
int width,
|
||||
int height);
|
||||
void calculateThresholdForBlock(unsigned char* luminances,
|
||||
ArrayRef<int> calculateBlackPoints(ArrayRef<char> luminances,
|
||||
int subWidth,
|
||||
int subHeight,
|
||||
int width,
|
||||
int height);
|
||||
void calculateThresholdForBlock(ArrayRef<char> luminances,
|
||||
int subWidth,
|
||||
int subHeight,
|
||||
int width,
|
||||
int height,
|
||||
int blackPoints[],
|
||||
ArrayRef<int> blackPoints,
|
||||
Ref<BitMatrix> const& matrix);
|
||||
void thresholdBlock(unsigned char* luminances,
|
||||
void thresholdBlock(ArrayRef<char>luminances,
|
||||
int xoffset,
|
||||
int yoffset,
|
||||
int threshold,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* String.cpp
|
||||
* zxing
|
||||
|
@ -20,19 +21,25 @@
|
|||
|
||||
#include <zxing/common/Str.h>
|
||||
|
||||
namespace zxing {
|
||||
using namespace std;
|
||||
using std::string;
|
||||
using zxing::String;
|
||||
using zxing::Ref;
|
||||
|
||||
String::String(const std::string &text) :
|
||||
text_(text) {
|
||||
text_(text) {
|
||||
}
|
||||
const std::string& String::getText() const {
|
||||
return text_;
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &out, const String &s) {
|
||||
char String::charAt(int i) const { return text_[i]; }
|
||||
int String::size() const { return text_.size(); }
|
||||
int String::length() const { return text_.size(); }
|
||||
Ref<String> String::substring(int i) const {
|
||||
return Ref<String>(new String(text_.substr(i)));
|
||||
}
|
||||
|
||||
std::ostream& zxing::operator << (std::ostream& out, String const& s) {
|
||||
out << s.text_;
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __STR_H__
|
||||
#define __STR_H__
|
||||
|
||||
|
@ -25,16 +26,21 @@
|
|||
#include <zxing/common/Counted.h>
|
||||
|
||||
namespace zxing {
|
||||
class String;
|
||||
std::ostream& operator << (std::ostream& out, String const& s);
|
||||
}
|
||||
|
||||
class String : public Counted {
|
||||
class zxing::String : public Counted {
|
||||
private:
|
||||
std::string text_;
|
||||
public:
|
||||
String(const std::string &text);
|
||||
explicit String(const std::string &text);
|
||||
char charAt(int) const;
|
||||
Ref<String> substring(int) const;
|
||||
const std::string &getText() const;
|
||||
friend std::ostream &operator<<(std::ostream &out, const String &s);
|
||||
int size() const;
|
||||
int length() const;
|
||||
friend std::ostream& zxing::operator << (std::ostream& out, String const& s);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __COMMON__STRING_H__
|
||||
|
|
|
@ -35,7 +35,7 @@ char const* const StringUtils::ISO88591 = "ISO8859-1";
|
|||
const bool StringUtils::ASSUME_SHIFT_JIS = false;
|
||||
|
||||
string
|
||||
StringUtils::guessEncoding(unsigned char* bytes, int length,
|
||||
StringUtils::guessEncoding(char* bytes, int length,
|
||||
Hashtable const& hints) {
|
||||
Hashtable::const_iterator i = hints.find(DecodeHints::CHARACTER_SET);
|
||||
if (i != hints.end()) {
|
||||
|
@ -64,7 +64,7 @@ StringUtils::guessEncoding(unsigned char* bytes, int length,
|
|||
//int isoHighChars = 0;
|
||||
int isoHighOther = 0;
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef char byte;
|
||||
boolean utf8bom = length > 3 &&
|
||||
bytes[0] == (byte) 0xEF &&
|
||||
bytes[1] == (byte) 0xBB &&
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
|
||||
typedef std::map<DecodeHintType, std::string> Hashtable;
|
||||
|
||||
static std::string guessEncoding(unsigned char* bytes, int length, Hashtable const& hints);
|
||||
static std::string guessEncoding(char* bytes, int length, Hashtable const& hints);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,27 +28,28 @@
|
|||
|
||||
namespace zxing {
|
||||
class GenericGF;
|
||||
|
||||
class GenericGFPoly : public Counted {
|
||||
private:
|
||||
Ref<GenericGF> field_;
|
||||
ArrayRef<int> coefficients_;
|
||||
|
||||
public:
|
||||
GenericGFPoly(Ref<GenericGF> field, ArrayRef<int> coefficients);
|
||||
ArrayRef<int> getCoefficients();
|
||||
int getDegree();
|
||||
bool isZero();
|
||||
int getCoefficient(int degree);
|
||||
int evaluateAt(int a);
|
||||
Ref<GenericGFPoly> addOrSubtract(Ref<GenericGFPoly> other);
|
||||
Ref<GenericGFPoly> multiply(Ref<GenericGFPoly> other);
|
||||
Ref<GenericGFPoly> multiply(int scalar);
|
||||
Ref<GenericGFPoly> multiplyByMonomial(int degree, int coefficient);
|
||||
std::vector<Ref<GenericGFPoly> > divide(Ref<GenericGFPoly> other);
|
||||
|
||||
//#warning todo: add print method
|
||||
};
|
||||
class GenericGFPoly;
|
||||
}
|
||||
|
||||
class zxing::GenericGFPoly : public Counted {
|
||||
private:
|
||||
Ref<GenericGF> field_;
|
||||
ArrayRef<int> coefficients_;
|
||||
|
||||
public:
|
||||
GenericGFPoly(Ref<GenericGF> field, ArrayRef<int> coefficients);
|
||||
ArrayRef<int> getCoefficients();
|
||||
int getDegree();
|
||||
bool isZero();
|
||||
int getCoefficient(int degree);
|
||||
int evaluateAt(int a);
|
||||
Ref<GenericGFPoly> addOrSubtract(Ref<GenericGFPoly> other);
|
||||
Ref<GenericGFPoly> multiply(Ref<GenericGFPoly> other);
|
||||
Ref<GenericGFPoly> multiply(int scalar);
|
||||
Ref<GenericGFPoly> multiplyByMonomial(int degree, int coefficient);
|
||||
std::vector<Ref<GenericGFPoly> > divide(Ref<GenericGFPoly> other);
|
||||
|
||||
//#warning todo: add print method
|
||||
};
|
||||
|
||||
#endif //GENERICGFPOLY_H
|
||||
#endif //GENERICGFPOLY_H
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* ReedSolomonDecoder.cpp
|
||||
* zxing
|
||||
|
@ -25,36 +26,23 @@
|
|||
#include <zxing/common/reedsolomon/ReedSolomonException.h>
|
||||
#include <zxing/common/IllegalArgumentException.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace std; // remove
|
||||
|
||||
namespace zxing {
|
||||
using zxing::Ref;
|
||||
using zxing::ArrayRef;
|
||||
using zxing::ReedSolomonDecoder;
|
||||
using zxing::GenericGFPoly;
|
||||
|
||||
ReedSolomonDecoder::ReedSolomonDecoder(Ref<GenericGF> fld) :
|
||||
field(fld) {
|
||||
field(fld) {
|
||||
}
|
||||
|
||||
ReedSolomonDecoder::~ReedSolomonDecoder() {
|
||||
}
|
||||
|
||||
void ReedSolomonDecoder::decode(ArrayRef<int> received, int twoS) {
|
||||
|
||||
Ref<GenericGFPoly> poly(new GenericGFPoly(field, received));
|
||||
|
||||
|
||||
/*
|
||||
#ifdef DEBUG
|
||||
cout << "decoding with poly " << *poly << endl;
|
||||
#endif
|
||||
*/
|
||||
|
||||
ArrayRef<int> syndromeCoefficients(new Array<int> (twoS));
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
cout << "syndromeCoefficients array = " <<
|
||||
syndromeCoefficients.array_ << endl;
|
||||
#endif
|
||||
|
||||
ArrayRef<int> syndromeCoefficients(twoS);
|
||||
bool dataMatrix = (field.object_ == GenericGF::DATA_MATRIX_FIELD_256.object_);
|
||||
bool noError = true;
|
||||
for (int i = 0; i < twoS; i++) {
|
||||
|
@ -67,17 +55,18 @@ void ReedSolomonDecoder::decode(ArrayRef<int> received, int twoS) {
|
|||
if (noError) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<GenericGFPoly> syndrome(new GenericGFPoly(field, syndromeCoefficients));
|
||||
Ref<GenericGFPoly> monomial = field->buildMonomial(twoS, 1);
|
||||
vector<Ref<GenericGFPoly> > 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++) {
|
||||
vector<Ref<GenericGFPoly> > sigmaOmega =
|
||||
runEuclideanAlgorithm(field->buildMonomial(twoS, 1), syndrome, twoS);
|
||||
Ref<GenericGFPoly> sigma = sigmaOmega[0];
|
||||
Ref<GenericGFPoly> omega = sigmaOmega[1];
|
||||
ArrayRef<int> errorLocations = findErrorLocations(sigma);
|
||||
ArrayRef<int> errorMagitudes = findErrorMagnitudes(omega, errorLocations, dataMatrix);
|
||||
for (int 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)");
|
||||
if (position < 0) {
|
||||
throw ReedSolomonException("Bad error location");
|
||||
}
|
||||
received[position] = GenericGF::addOrSubtract(received[position], errorMagitudes[i]);
|
||||
}
|
||||
}
|
||||
|
@ -137,12 +126,12 @@ vector<Ref<GenericGFPoly> > ReedSolomonDecoder::runEuclideanAlgorithm(Ref<Generi
|
|||
|
||||
|
||||
/*
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG
|
||||
cout << "t = " << *t << endl;
|
||||
cout << "r = " << *r << "\n";
|
||||
cout << "sigma = " << *sigma << endl;
|
||||
cout << "omega = " << *omega << endl;
|
||||
#endif
|
||||
#endif
|
||||
*/
|
||||
|
||||
vector<Ref<GenericGFPoly> > result(2);
|
||||
|
@ -184,15 +173,14 @@ ArrayRef<int> ReedSolomonDecoder::findErrorMagnitudes(Ref<GenericGFPoly> errorEv
|
|||
for (int j = 0; j < s; j++) {
|
||||
if (i != j) {
|
||||
denominator = field->multiply(denominator, GenericGF::addOrSubtract(1, field->multiply(errorLocations[j],
|
||||
xiInverse)));
|
||||
xiInverse)));
|
||||
}
|
||||
}
|
||||
result[i] = field->multiply(errorEvaluator->evaluateAt(xiInverse), field->inverse(denominator));
|
||||
|
||||
if (dataMatrix) {
|
||||
result[i] = field->multiply(result[i], xiInverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ Ref<Result> DataMatrixReader::decode(Ref<BinaryBitmap> image, DecodeHints hints)
|
|||
cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush;
|
||||
#endif
|
||||
|
||||
std::vector<Ref<ResultPoint> > points(detectorResult->getPoints());
|
||||
ArrayRef< Ref<ResultPoint> > points(detectorResult->getPoints());
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -69,7 +69,7 @@ Ref<Result> DataMatrixReader::decode(Ref<BinaryBitmap> image, DecodeHints hints)
|
|||
#endif
|
||||
|
||||
Ref<Result> result(
|
||||
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_DATA_MATRIX));
|
||||
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat::DATA_MATRIX));
|
||||
#ifdef DEBUG
|
||||
cout << "(5) created result " << result.object_ << ", returning\n" << flush;
|
||||
#endif
|
||||
|
|
|
@ -57,9 +57,9 @@ Ref<Version> BitMatrixParser::readVersion(Ref<BitMatrix> bitMatrix) {
|
|||
throw ReaderException("Couldn't decode version");
|
||||
}
|
||||
|
||||
ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
|
||||
ArrayRef<unsigned char> result(parsedVersion_->getTotalCodewords());
|
||||
int resultOffset = 0;
|
||||
ArrayRef<char> BitMatrixParser::readCodewords() {
|
||||
ArrayRef<char> result(parsedVersion_->getTotalCodewords());
|
||||
int resultOffset = 0;
|
||||
int row = 4;
|
||||
int column = 0;
|
||||
|
||||
|
@ -75,22 +75,22 @@ ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
|
|||
do {
|
||||
// Check the four corner cases
|
||||
if ((row == numRows) && (column == 0) && !corner1Read) {
|
||||
result[resultOffset++] = (unsigned char) readCorner1(numRows, numColumns);
|
||||
result[resultOffset++] = (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);
|
||||
result[resultOffset++] = (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);
|
||||
result[resultOffset++] = (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);
|
||||
result[resultOffset++] = (char) readCorner4(numRows, numColumns);
|
||||
row -= 2;
|
||||
column +=2;
|
||||
corner4Read = true;
|
||||
|
@ -98,7 +98,7 @@ ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
|
|||
// 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);
|
||||
result[resultOffset++] = (char) readUtah(row, column, numRows, numColumns);
|
||||
}
|
||||
row -= 2;
|
||||
column +=2;
|
||||
|
@ -109,7 +109,7 @@ ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
|
|||
// 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);
|
||||
result[resultOffset++] = (char) readUtah(row, column, numRows, numColumns);
|
||||
}
|
||||
row += 2;
|
||||
column -=2;
|
||||
|
|
|
@ -41,7 +41,7 @@ private:
|
|||
public:
|
||||
BitMatrixParser(Ref<BitMatrix> bitMatrix);
|
||||
Ref<Version> readVersion(Ref<BitMatrix> bitMatrix);
|
||||
ArrayRef<unsigned char> readCodewords();
|
||||
ArrayRef<char> readCodewords();
|
||||
bool readModule(int row, int column, int numRows, int numColumns);
|
||||
|
||||
private:
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace datamatrix {
|
|||
|
||||
using namespace std;
|
||||
|
||||
DataBlock::DataBlock(int numDataCodewords, ArrayRef<unsigned char> codewords) :
|
||||
DataBlock::DataBlock(int numDataCodewords, ArrayRef<char> codewords) :
|
||||
numDataCodewords_(numDataCodewords), codewords_(codewords) {
|
||||
}
|
||||
|
||||
|
@ -34,11 +34,11 @@ int DataBlock::getNumDataCodewords() {
|
|||
return numDataCodewords_;
|
||||
}
|
||||
|
||||
ArrayRef<unsigned char> DataBlock::getCodewords() {
|
||||
ArrayRef<char> DataBlock::getCodewords() {
|
||||
return codewords_;
|
||||
}
|
||||
|
||||
std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<unsigned char> rawCodewords, Version *version) {
|
||||
std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<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();
|
||||
|
@ -58,7 +58,7 @@ std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<unsigned char> ra
|
|||
for (int i = 0; i < ecBlock->getCount(); i++) {
|
||||
int numDataCodewords = ecBlock->getDataCodewords();
|
||||
int numBlockCodewords = ecBlocks->getECCodewords() + numDataCodewords;
|
||||
ArrayRef<unsigned char> buffer(numBlockCodewords);
|
||||
ArrayRef<char> buffer(numBlockCodewords);
|
||||
Ref<DataBlock> blockRef(new DataBlock(numDataCodewords, buffer));
|
||||
result[numResultBlocks++] = blockRef;
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<unsigned char> ra
|
|||
}
|
||||
}
|
||||
|
||||
if ((size_t)rawCodewordsOffset != rawCodewords.size()) {
|
||||
if (rawCodewordsOffset != rawCodewords.size()) {
|
||||
throw IllegalArgumentException("rawCodewordsOffset != rawCodewords.length");
|
||||
}
|
||||
|
||||
|
|
|
@ -32,15 +32,15 @@ namespace datamatrix {
|
|||
class DataBlock : public Counted {
|
||||
private:
|
||||
int numDataCodewords_;
|
||||
ArrayRef<unsigned char> codewords_;
|
||||
ArrayRef<char> codewords_;
|
||||
|
||||
DataBlock(int numDataCodewords, ArrayRef<unsigned char> codewords);
|
||||
DataBlock(int numDataCodewords, ArrayRef<char> codewords);
|
||||
|
||||
public:
|
||||
static std::vector<Ref<DataBlock> > getDataBlocks(ArrayRef<unsigned char> rawCodewords, Version *version);
|
||||
static std::vector<Ref<DataBlock> > getDataBlocks(ArrayRef<char> rawCodewords, Version *version);
|
||||
|
||||
int getNumDataCodewords();
|
||||
ArrayRef<unsigned char> getCodewords();
|
||||
ArrayRef<char> getCodewords();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -51,11 +51,11 @@ const char DecodedBitStreamParser::TEXT_SHIFT3_SET_CHARS[] = {
|
|||
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127
|
||||
};
|
||||
|
||||
Ref<DecoderResult> DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes) {
|
||||
Ref<DecoderResult> DecodedBitStreamParser::decode(ArrayRef<char> bytes) {
|
||||
Ref<BitSource> bits(new BitSource(bytes));
|
||||
ostringstream result;
|
||||
ostringstream resultTrailer;
|
||||
vector<unsigned char> byteSegments;
|
||||
vector<char> byteSegments;
|
||||
int mode = ASCII_ENCODE;
|
||||
do {
|
||||
if (mode == ASCII_ENCODE) {
|
||||
|
@ -87,7 +87,7 @@ Ref<DecoderResult> DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes)
|
|||
if (resultTrailer.str().size() > 0) {
|
||||
result << resultTrailer.str();
|
||||
}
|
||||
ArrayRef<unsigned char> rawBytes(bytes);
|
||||
ArrayRef<char> rawBytes(bytes);
|
||||
Ref<String> text(new String(result.str()));
|
||||
return Ref<DecoderResult>(new DecoderResult(rawBytes, text));
|
||||
}
|
||||
|
@ -381,7 +381,7 @@ void DecodedBitStreamParser::decodeEdifactSegment(Ref<BitSource> bits, ostringst
|
|||
} while (bits->available() > 0);
|
||||
}
|
||||
|
||||
void DecodedBitStreamParser::decodeBase256Segment(Ref<BitSource> bits, ostringstream& result, vector<unsigned char> byteSegments) {
|
||||
void DecodedBitStreamParser::decodeBase256Segment(Ref<BitSource> bits, ostringstream& result, vector<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++);
|
||||
|
@ -399,7 +399,7 @@ void DecodedBitStreamParser::decodeBase256Segment(Ref<BitSource> bits, ostringst
|
|||
throw FormatException("NegativeArraySizeException");
|
||||
}
|
||||
|
||||
unsigned char* bytes = new unsigned char[count];
|
||||
char* bytes = new 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
|
||||
|
|
|
@ -79,23 +79,23 @@ private:
|
|||
/**
|
||||
* See ISO 16022:2006, 5.2.9 and Annex B, B.2
|
||||
*/
|
||||
void decodeBase256Segment(Ref<BitSource> bits, std::ostringstream &result, std::vector<unsigned char> byteSegments);
|
||||
void decodeBase256Segment(Ref<BitSource> bits, std::ostringstream &result, std::vector<char> byteSegments);
|
||||
|
||||
void parseTwoBytes(int firstByte, int secondByte, int* result);
|
||||
/**
|
||||
* See ISO 16022:2006, Annex B, B.2
|
||||
*/
|
||||
unsigned char unrandomize255State(int randomizedBase256Codeword,
|
||||
int base256CodewordPosition) {
|
||||
char unrandomize255State(int randomizedBase256Codeword,
|
||||
int base256CodewordPosition) {
|
||||
int pseudoRandomNumber = ((149 * base256CodewordPosition) % 255) + 1;
|
||||
int tempVariable = randomizedBase256Codeword - pseudoRandomNumber;
|
||||
return (unsigned char) (tempVariable >= 0 ? tempVariable : (tempVariable + 256));
|
||||
return (char) (tempVariable >= 0 ? tempVariable : (tempVariable + 256));
|
||||
};
|
||||
void append(std::ostream &ost, const unsigned char *bufIn, size_t nIn, const char *src);
|
||||
void append(std::ostream &ost, const char *bufIn, size_t nIn, const char *src);
|
||||
|
||||
public:
|
||||
DecodedBitStreamParser() { };
|
||||
Ref<DecoderResult> decode(ArrayRef<unsigned char> bytes);
|
||||
Ref<DecoderResult> decode(ArrayRef<char> bytes);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ Decoder::Decoder() :
|
|||
}
|
||||
|
||||
|
||||
void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCodewords) {
|
||||
void Decoder::correctErrors(ArrayRef<char> codewordBytes, int numDataCodewords) {
|
||||
int numCodewords = codewordBytes->size();
|
||||
ArrayRef<int> codewordInts(numCodewords);
|
||||
for (int i = 0; i < numCodewords; i++) {
|
||||
|
@ -52,7 +52,7 @@ void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCo
|
|||
// 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];
|
||||
codewordBytes[i] = (char)codewordInts[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
|
|||
Version *version = parser.readVersion(bits);
|
||||
|
||||
// Read codewords
|
||||
ArrayRef<unsigned char> codewords(parser.readCodewords());
|
||||
ArrayRef<char> codewords(parser.readCodewords());
|
||||
// Separate into data blocks
|
||||
std::vector<Ref<DataBlock> > dataBlocks = DataBlock::getDataBlocks(codewords, version);
|
||||
|
||||
|
@ -73,12 +73,12 @@ Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
|
|||
for (int i = 0; i < dataBlocksCount; i++) {
|
||||
totalBytes += dataBlocks[i]->getNumDataCodewords();
|
||||
}
|
||||
ArrayRef<unsigned char> resultBytes(totalBytes);
|
||||
ArrayRef<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();
|
||||
ArrayRef<char> codewordBytes = dataBlock->getCodewords();
|
||||
int numDataCodewords = dataBlock->getNumDataCodewords();
|
||||
correctErrors(codewordBytes, numDataCodewords);
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
|
|
|
@ -35,7 +35,7 @@ class Decoder {
|
|||
private:
|
||||
ReedSolomonDecoder rsDecoder_;
|
||||
|
||||
void correctErrors(ArrayRef<unsigned char> bytes, int numDataCodewords);
|
||||
void correctErrors(ArrayRef<char> bytes, int numDataCodewords);
|
||||
|
||||
public:
|
||||
Decoder();
|
||||
|
|
|
@ -245,7 +245,7 @@ Ref<DetectorResult> Detector::detect() {
|
|||
bits = sampleGrid(image_, dimensionCorrected, dimensionCorrected, transform);
|
||||
}
|
||||
|
||||
std::vector<Ref<ResultPoint> > points(4);
|
||||
ArrayRef< Ref<ResultPoint> > points (new Array< Ref<ResultPoint> >(4));
|
||||
points[0].reset(topLeft);
|
||||
points[1].reset(bottomLeft);
|
||||
points[2].reset(correctedTopRight);
|
||||
|
|
|
@ -60,8 +60,8 @@ void GenericMultipleBarcodeReader::doDecodeMultiple(Ref<BinaryBitmap> image,
|
|||
}
|
||||
|
||||
results.push_back(translateResultPoints(result, xOffset, yOffset));
|
||||
const std::vector<Ref<ResultPoint> > resultPoints = result->getResultPoints();
|
||||
if (resultPoints.empty()) {
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints = result->getResultPoints();
|
||||
if (resultPoints->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ void GenericMultipleBarcodeReader::doDecodeMultiple(Ref<BinaryBitmap> image,
|
|||
float minY = height;
|
||||
float maxX = 0.0f;
|
||||
float maxY = 0.0f;
|
||||
for (unsigned int i = 0; i < resultPoints.size(); i++) {
|
||||
for (int i = 0; i < resultPoints.size(); i++) {
|
||||
Ref<ResultPoint> point = resultPoints[i];
|
||||
float x = point->getX();
|
||||
float y = point->getY();
|
||||
|
@ -112,14 +112,14 @@ void GenericMultipleBarcodeReader::doDecodeMultiple(Ref<BinaryBitmap> image,
|
|||
}
|
||||
|
||||
Ref<Result> GenericMultipleBarcodeReader::translateResultPoints(Ref<Result> result, int xOffset, int yOffset){
|
||||
const std::vector<Ref<ResultPoint> > oldResultPoints = result->getResultPoints();
|
||||
if (oldResultPoints.empty()) {
|
||||
ArrayRef< Ref<ResultPoint> > oldResultPoints = result->getResultPoints();
|
||||
if (oldResultPoints->empty()) {
|
||||
return result;
|
||||
}
|
||||
std::vector<Ref<ResultPoint> > newResultPoints;
|
||||
for (unsigned int i = 0; i < oldResultPoints.size(); i++) {
|
||||
ArrayRef< Ref<ResultPoint> > newResultPoints;
|
||||
for (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)));
|
||||
newResultPoints->values().push_back(Ref<ResultPoint>(new ResultPoint(oldPoint->getX() + xOffset, oldPoint->getY() + yOffset)));
|
||||
}
|
||||
return Ref<Result>(new Result(result->getText(), result->getRawBytes(), newResultPoints, result->getBarcodeFormat()));
|
||||
}
|
||||
|
|
|
@ -36,10 +36,10 @@ std::vector<Ref<Result> > QRCodeMultiReader::decodeMultiple(Ref<BinaryBitmap> im
|
|||
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();
|
||||
ArrayRef< Ref<ResultPoint> > points = detectorResult[i]->getPoints();
|
||||
Ref<Result> result = Ref<Result>(new Result(decoderResult->getText(),
|
||||
decoderResult->getRawBytes(),
|
||||
points, BarcodeFormat_QR_CODE));
|
||||
points, BarcodeFormat::QR_CODE));
|
||||
// result->putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult->getByteSegments());
|
||||
// result->putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult->getECLevel().toString());
|
||||
results.push_back(result);
|
||||
|
|
328
cpp/core/src/zxing/oned/CodaBarReader.cpp
Normal file
328
cpp/core/src/zxing/oned/CodaBarReader.cpp
Normal file
|
@ -0,0 +1,328 @@
|
|||
// -*- 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 "CodaBarReader.h"
|
||||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
#include <zxing/FormatException.h>
|
||||
#include <zxing/ChecksumException.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using zxing::NotFoundException;
|
||||
using zxing::FormatException;
|
||||
using zxing::ChecksumException;
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
using zxing::oned::CodaBarReader;
|
||||
|
||||
namespace {
|
||||
char const ALPHABET_STRING[] = "0123456789-$:/.+ABCD";
|
||||
char const* const ALPHABET = ALPHABET_STRING;
|
||||
|
||||
/**
|
||||
* These represent the encodings of characters, as patterns of wide and narrow bars. The 7 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[] = {
|
||||
0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, // 0-9
|
||||
0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E, // -$:/.+ABCD
|
||||
};
|
||||
|
||||
// minimal number of characters that should be present (inclusing start and stop characters)
|
||||
// under normal circumstances this should be set to 3, but can be set higher
|
||||
// as a last-ditch attempt to reduce false positives.
|
||||
const int MIN_CHARACTER_LENGTH = 3;
|
||||
|
||||
// official start and end patterns
|
||||
const char STARTEND_ENCODING[] = {'A', 'B', 'C', 'D', 0};
|
||||
// some codabar generator allow the codabar string to be closed by every
|
||||
// character. This will cause lots of false positives!
|
||||
|
||||
// some industries use a checksum standard but this is not part of the original codabar standard
|
||||
// for more information see : http://www.mecsw.com/specs/codabar.html
|
||||
}
|
||||
|
||||
// These values are critical for determining how permissive the decoding
|
||||
// will be. All stripe sizes must be within the window these define, as
|
||||
// compared to the average stripe size.
|
||||
const int CodaBarReader::MAX_ACCEPTABLE =
|
||||
(int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 2.0f);
|
||||
const int CodaBarReader::PADDING =
|
||||
(int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 1.5f);
|
||||
|
||||
CodaBarReader::CodaBarReader()
|
||||
: counters(80, 0), counterLength(0) {}
|
||||
|
||||
using namespace std;
|
||||
|
||||
Ref<Result> CodaBarReader::decodeRow(int rowNumber,
|
||||
Ref<BitArray> row) {
|
||||
// cerr << "cbr " << rowNumber << " " << *row << endl;
|
||||
setCounters(row);
|
||||
int startOffset = findStartPattern();
|
||||
int nextStart = startOffset;
|
||||
|
||||
decodeRowResult.clear();
|
||||
do {
|
||||
int charOffset = toNarrowWidePattern(nextStart);
|
||||
if (charOffset == -1) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
// Hack: We store the position in the alphabet table into a
|
||||
// StringBuilder, so that we can access the decoded patterns in
|
||||
// validatePattern. We'll translate to the actual characters later.
|
||||
decodeRowResult.append(1, (char)charOffset);
|
||||
nextStart += 8;
|
||||
// Stop as soon as we see the end character.
|
||||
if (decodeRowResult.length() > 1 &&
|
||||
arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {
|
||||
break;
|
||||
}
|
||||
} while (nextStart < counterLength); // no fixed end pattern so keep on reading while data is available
|
||||
|
||||
// Look for whitespace after pattern:
|
||||
int trailingWhitespace = counters[nextStart - 1];
|
||||
int lastPatternSize = 0;
|
||||
for (int i = -8; i < -1; i++) {
|
||||
lastPatternSize += counters[nextStart + i];
|
||||
}
|
||||
|
||||
// We need to see whitespace equal to 50% of the last pattern size,
|
||||
// otherwise this is probably a false positive. The exception is if we are
|
||||
// at the end of the row. (I.e. the barcode barely fits.)
|
||||
if (nextStart < counterLength && trailingWhitespace < lastPatternSize / 2) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
validatePattern(startOffset);
|
||||
|
||||
// Translate character table offsets to actual characters.
|
||||
for (int i = 0; i < (int)decodeRowResult.length(); i++) {
|
||||
decodeRowResult[i] = ALPHABET[(int)decodeRowResult[i]];
|
||||
}
|
||||
// Ensure a valid start and end character
|
||||
char startchar = decodeRowResult[0];
|
||||
if (!arrayContains(STARTEND_ENCODING, startchar)) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
char endchar = decodeRowResult[decodeRowResult.length() - 1];
|
||||
if (!arrayContains(STARTEND_ENCODING, endchar)) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
// remove stop/start characters character and check if a long enough string is contained
|
||||
if ((int)decodeRowResult.length() <= MIN_CHARACTER_LENGTH) {
|
||||
// Almost surely a false positive ( start + stop + at least 1 character)
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
decodeRowResult.erase(decodeRowResult.length() - 1, 1);
|
||||
decodeRowResult.erase(0, 1);
|
||||
|
||||
int runningCount = 0;
|
||||
for (int i = 0; i < startOffset; i++) {
|
||||
runningCount += counters[i];
|
||||
}
|
||||
float left = (float) runningCount;
|
||||
for (int i = startOffset; i < nextStart - 1; i++) {
|
||||
runningCount += counters[i];
|
||||
}
|
||||
float right = (float) runningCount;
|
||||
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints(2);
|
||||
resultPoints[0] =
|
||||
Ref<OneDResultPoint>(new OneDResultPoint(left, (float) rowNumber));
|
||||
resultPoints[1] =
|
||||
Ref<OneDResultPoint>(new OneDResultPoint(right, (float) rowNumber));
|
||||
|
||||
return Ref<Result>(
|
||||
new Result(
|
||||
Ref<String>(new String(decodeRowResult)),
|
||||
ArrayRef<char>(),
|
||||
resultPoints,
|
||||
BarcodeFormat::CODABAR)
|
||||
);
|
||||
}
|
||||
|
||||
void CodaBarReader::validatePattern(int start) {
|
||||
// First, sum up the total size of our four categories of stripe sizes;
|
||||
vector<int> sizes (4, 0);
|
||||
vector<int> counts (4, 0);
|
||||
int end = decodeRowResult.length() - 1;
|
||||
|
||||
// We break out of this loop in the middle, in order to handle
|
||||
// inter-character spaces properly.
|
||||
int pos = start;
|
||||
for (int i = 0; true; i++) {
|
||||
int pattern = CHARACTER_ENCODINGS[(int)decodeRowResult[i]];
|
||||
for (int j = 6; j >= 0; j--) {
|
||||
// Even j = bars, while odd j = spaces. Categories 2 and 3 are for
|
||||
// long stripes, while 0 and 1 are for short stripes.
|
||||
int category = (j & 1) + (pattern & 1) * 2;
|
||||
sizes[category] += counters[pos + j];
|
||||
counts[category]++;
|
||||
pattern >>= 1;
|
||||
}
|
||||
if (i >= end) {
|
||||
break;
|
||||
}
|
||||
// We ignore the inter-character space - it could be of any size.
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
// Calculate our allowable size thresholds using fixed-point math.
|
||||
vector<int> maxes (4, 0);
|
||||
vector<int> mins (4, 0);
|
||||
// Define the threshold of acceptability to be the midpoint between the
|
||||
// average small stripe and the average large stripe. No stripe lengths
|
||||
// should be on the "wrong" side of that line.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
mins[i] = 0; // Accept arbitrarily small "short" stripes.
|
||||
mins[i + 2] = ((sizes[i] << INTEGER_MATH_SHIFT) / counts[i] +
|
||||
(sizes[i + 2] << INTEGER_MATH_SHIFT) / counts[i + 2]) >> 1;
|
||||
maxes[i] = mins[i + 2];
|
||||
maxes[i + 2] = (sizes[i + 2] * MAX_ACCEPTABLE + PADDING) / counts[i + 2];
|
||||
}
|
||||
|
||||
// Now verify that all of the stripes are within the thresholds.
|
||||
pos = start;
|
||||
for (int i = 0; true; i++) {
|
||||
int pattern = CHARACTER_ENCODINGS[(int)decodeRowResult[i]];
|
||||
for (int j = 6; j >= 0; j--) {
|
||||
// Even j = bars, while odd j = spaces. Categories 2 and 3 are for
|
||||
// long stripes, while 0 and 1 are for short stripes.
|
||||
int category = (j & 1) + (pattern & 1) * 2;
|
||||
int size = counters[pos + j] << INTEGER_MATH_SHIFT;
|
||||
if (size < mins[category] || size > maxes[category]) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
pattern >>= 1;
|
||||
}
|
||||
if (i >= end) {
|
||||
break;
|
||||
}
|
||||
pos += 8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the size of all runs of white and black pixels, starting with white.
|
||||
* This is just like recordPattern, except it records all the counters, and
|
||||
* uses our builtin "counters" member for storage.
|
||||
* @param row row to count from
|
||||
*/
|
||||
void CodaBarReader::setCounters(Ref<BitArray> row) {
|
||||
counterLength = 0;
|
||||
// Start from the first white bit.
|
||||
int i = row->getNextUnset(0);
|
||||
int end = row->getSize();
|
||||
if (i >= end) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
bool isWhite = true;
|
||||
int count = 0;
|
||||
for (; i < end; i++) {
|
||||
if (row->get(i) ^ isWhite) { // that is, exactly one is true
|
||||
count++;
|
||||
} else {
|
||||
counterAppend(count);
|
||||
count = 1;
|
||||
isWhite = !isWhite;
|
||||
}
|
||||
}
|
||||
counterAppend(count);
|
||||
}
|
||||
|
||||
void CodaBarReader::counterAppend(int e) {
|
||||
if (counterLength < (int)counters.size()) {
|
||||
counters[counterLength] = e;
|
||||
} else {
|
||||
counters.push_back(e);
|
||||
}
|
||||
counterLength++;
|
||||
}
|
||||
|
||||
int CodaBarReader::findStartPattern() {
|
||||
for (int i = 1; i < counterLength; i += 2) {
|
||||
int charOffset = toNarrowWidePattern(i);
|
||||
if (charOffset != -1 && arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {
|
||||
// Look for whitespace before start pattern, >= 50% of width of start pattern
|
||||
// We make an exception if the whitespace is the first element.
|
||||
int patternSize = 0;
|
||||
for (int j = i; j < i + 7; j++) {
|
||||
patternSize += counters[j];
|
||||
}
|
||||
if (i == 1 || counters[i-1] >= patternSize / 2) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
bool CodaBarReader::arrayContains(char const array[], char key) {
|
||||
return index(array, key) != 0;
|
||||
}
|
||||
|
||||
// Assumes that counters[position] is a bar.
|
||||
int CodaBarReader::toNarrowWidePattern(int position) {
|
||||
int end = position + 7;
|
||||
if (end >= counterLength) {
|
||||
return -1;
|
||||
}
|
||||
// First element is for bars, second is for spaces.
|
||||
vector<int> maxes (2, 0);
|
||||
vector<int> mins (2, std::numeric_limits<int>::max());
|
||||
vector<int> thresholds (2, 0);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = position + i; j < end; j += 2) {
|
||||
if (counters[j] < mins[i]) {
|
||||
mins[i] = counters[j];
|
||||
}
|
||||
if (counters[j] > maxes[i]) {
|
||||
maxes[i] = counters[j];
|
||||
}
|
||||
}
|
||||
thresholds[i] = (mins[i] + maxes[i]) / 2;
|
||||
}
|
||||
|
||||
int bitmask = 1 << 7;
|
||||
int pattern = 0;
|
||||
for (int i = 0; i < 7; i++) {
|
||||
int barOrSpace = i & 1;
|
||||
bitmask >>= 1;
|
||||
if (counters[position + i] > thresholds[barOrSpace]) {
|
||||
pattern |= bitmask;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0;
|
||||
i < (int)(sizeof(CHARACTER_ENCODINGS)/sizeof(CHARACTER_ENCODINGS[0]));
|
||||
i++) {
|
||||
if (CHARACTER_ENCODINGS[i] == pattern) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
57
cpp/core/src/zxing/oned/CodaBarReader.h
Normal file
57
cpp/core/src/zxing/oned/CodaBarReader.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __CODA_BAR_READER_H__
|
||||
#define __CODA_BAR_READER_H__
|
||||
/*
|
||||
* 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/oned/OneDReader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class CodaBarReader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::CodaBarReader : public OneDReader {
|
||||
private:
|
||||
static const int MAX_ACCEPTABLE;
|
||||
static const int PADDING;
|
||||
|
||||
// Keep some instance variables to avoid reallocations
|
||||
std::string decodeRowResult;
|
||||
std::vector<int> counters;
|
||||
int counterLength;
|
||||
|
||||
public:
|
||||
CodaBarReader();
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
|
||||
void validatePattern(int start);
|
||||
|
||||
private:
|
||||
void setCounters(Ref<BitArray> row);
|
||||
void counterAppend(int e);
|
||||
int findStartPattern();
|
||||
|
||||
static bool arrayContains(char const array[], char key);
|
||||
|
||||
int toNarrowWidePattern(int position);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -19,471 +19,457 @@
|
|||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
#include <zxing/FormatException.h>
|
||||
#include <zxing/ChecksumException.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using zxing::NotFoundException;
|
||||
using zxing::FormatException;
|
||||
using zxing::ChecksumException;
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
using zxing::oned::Code128Reader;
|
||||
|
||||
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}
|
||||
};
|
||||
const int Code128Reader::MAX_AVG_VARIANCE = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 250/1000);
|
||||
const int Code128Reader::MAX_INDIVIDUAL_VARIANCE = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 700/1000);
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Look for whitespace before start pattern, >= 50% of width of start pattern
|
||||
if (bestMatch >= 0 &&
|
||||
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(){
|
||||
}
|
||||
}
|
||||
namespace {
|
||||
const int CODE_SHIFT = 98;
|
||||
|
||||
const int CODE_CODE_C = 99;
|
||||
const int CODE_CODE_B = 100;
|
||||
const int CODE_CODE_A = 101;
|
||||
|
||||
const int CODE_FNC_1 = 102;
|
||||
const int CODE_FNC_2 = 97;
|
||||
const int CODE_FNC_3 = 96;
|
||||
const int CODE_FNC_4_A = 101;
|
||||
const int CODE_FNC_4_B = 100;
|
||||
|
||||
const int CODE_START_A = 103;
|
||||
const int CODE_START_B = 104;
|
||||
const int CODE_START_C = 105;
|
||||
const int CODE_STOP = 106;
|
||||
|
||||
const int CODE_PATTERNS_LENGTH = 107;
|
||||
const int CODE_PATTERNS[CODE_PATTERNS_LENGTH][6] = {
|
||||
{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(){}
|
||||
|
||||
vector<int> Code128Reader::findStartPattern(Ref<BitArray> row){
|
||||
int width = row->getSize();
|
||||
int rowOffset = row->getNextSet(0);
|
||||
|
||||
int counterPosition = 0;
|
||||
vector<int> counters (6, 0);
|
||||
int patternStart = rowOffset;
|
||||
bool isWhite = false;
|
||||
int patternLength = counters.size();
|
||||
|
||||
for (int i = rowOffset; i < width; i++) {
|
||||
if (row->get(i) ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
int bestVariance = MAX_AVG_VARIANCE;
|
||||
int bestMatch = -1;
|
||||
for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
|
||||
int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE);
|
||||
if (variance < bestVariance) {
|
||||
bestVariance = variance;
|
||||
bestMatch = startCode;
|
||||
}
|
||||
}
|
||||
// Look for whitespace before start pattern, >= 50% of width of start pattern
|
||||
if (bestMatch >= 0 &&
|
||||
row->isRange(std::max(0, patternStart - (i - patternStart) / 2), patternStart, false)) {
|
||||
vector<int> resultValue (3, 0);
|
||||
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 NotFoundException();
|
||||
}
|
||||
|
||||
int Code128Reader::decodeCode(Ref<BitArray> row, vector<int>& counters, int rowOffset) {
|
||||
recordPattern(row, rowOffset, counters);
|
||||
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
||||
int bestMatch = -1;
|
||||
for (int d = 0; d < CODE_PATTERNS_LENGTH; d++) {
|
||||
int const* const pattern = CODE_PATTERNS[d];
|
||||
int variance = patternMatchVariance(counters, 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 NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Result> Code128Reader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
||||
vector<int> 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 FormatException();
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
bool isNextShifted = false;
|
||||
|
||||
string result;
|
||||
vector<char> rawCodes(20, 0);
|
||||
|
||||
int lastStart = startPatternInfo[0];
|
||||
int nextStart = startPatternInfo[1];
|
||||
vector<int> counters (6, 0);
|
||||
|
||||
int lastCode = 0;
|
||||
int code = 0;
|
||||
int checksumTotal = startCode;
|
||||
int multiplier = 0;
|
||||
bool lastCharacterWasPrintable = true;
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
while (!done) {
|
||||
|
||||
bool unshift = isNextShifted;
|
||||
isNextShifted = false;
|
||||
|
||||
// Save off last code
|
||||
lastCode = code;
|
||||
|
||||
code = decodeCode(row, counters, nextStart);
|
||||
|
||||
// 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;
|
||||
for (int i = 0, e = counters.size(); i < e; 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 FormatException();
|
||||
}
|
||||
|
||||
switch (codeSet) {
|
||||
|
||||
case CODE_CODE_A:
|
||||
if (code < 64) {
|
||||
result.append(1, (char) (' ' + code));
|
||||
} else if (code < 96) {
|
||||
result.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) {
|
||||
result.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_A;
|
||||
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:
|
||||
if (code < 100) {
|
||||
if (code < 10) {
|
||||
result.append(1, '0');
|
||||
}
|
||||
oss.clear();
|
||||
oss.str("");
|
||||
oss << code;
|
||||
result.append(oss.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) {
|
||||
codeSet = codeSet == CODE_CODE_A ? CODE_CODE_B : CODE_CODE_A;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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:
|
||||
nextStart = row->getNextUnset(nextStart);
|
||||
if (!row->isRange(nextStart,
|
||||
std::min(row->getSize(), nextStart + (nextStart - lastStart) / 2),
|
||||
false)) {
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
||||
// Pull out from sum the value of the penultimate check code
|
||||
checksumTotal -= multiplier * lastCode;
|
||||
// lastCode is the checksum then:
|
||||
if (checksumTotal % 103 != lastCode) {
|
||||
throw ChecksumException();
|
||||
}
|
||||
|
||||
// Need to pull out the check digits from string
|
||||
int resultLength = result.length();
|
||||
if (resultLength == 0) {
|
||||
// false positive
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
result.erase(resultLength - 2, resultLength);
|
||||
} else {
|
||||
result.erase(resultLength - 1, resultLength);
|
||||
}
|
||||
}
|
||||
|
||||
float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;
|
||||
float right = (float) (nextStart + lastStart) / 2.0f;
|
||||
|
||||
int rawCodesSize = rawCodes.size();
|
||||
ArrayRef<char> rawBytes (rawCodesSize);
|
||||
for (int i = 0; i < rawCodesSize; i++) {
|
||||
rawBytes[i] = rawCodes[i];
|
||||
}
|
||||
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints(2);
|
||||
resultPoints[0] =
|
||||
Ref<OneDResultPoint>(new OneDResultPoint(left, (float) rowNumber));
|
||||
resultPoints[1] =
|
||||
Ref<OneDResultPoint>(new OneDResultPoint(right, (float) rowNumber));
|
||||
|
||||
return Ref<Result>(new Result(Ref<String>(new String(result)), rawBytes, resultPoints,
|
||||
BarcodeFormat::CODE_128));
|
||||
}
|
||||
|
||||
Code128Reader::~Code128Reader(){}
|
||||
|
||||
zxing::BarcodeFormat Code128Reader::getBarcodeFormat(){
|
||||
return BarcodeFormat::CODE_128;
|
||||
}
|
||||
|
|
|
@ -23,38 +23,26 @@
|
|||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class Code128Reader : public OneDReader {
|
||||
|
||||
private:
|
||||
enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 250/1000)};
|
||||
enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 700/1000)};
|
||||
static const int CODE_SHIFT = 98;
|
||||
|
||||
static const int CODE_CODE_C = 99;
|
||||
static const int CODE_CODE_B = 100;
|
||||
static const int CODE_CODE_A = 101;
|
||||
|
||||
static const int CODE_FNC_1 = 102;
|
||||
static const int CODE_FNC_2 = 97;
|
||||
static const int CODE_FNC_3 = 96;
|
||||
static const int CODE_FNC_4_A = 101;
|
||||
static const int CODE_FNC_4_B = 100;
|
||||
|
||||
static const int CODE_START_A = 103;
|
||||
static const int CODE_START_B = 104;
|
||||
static const int CODE_START_C = 105;
|
||||
static const int CODE_STOP = 106;
|
||||
|
||||
static int* findStartPattern(Ref<BitArray> row);
|
||||
static int decodeCode(Ref<BitArray> row, int counters[], int countersCount, int rowOffset);
|
||||
|
||||
void append(char* s, char c);
|
||||
public:
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
Code128Reader();
|
||||
~Code128Reader();
|
||||
};
|
||||
}
|
||||
class Code128Reader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::Code128Reader : public OneDReader {
|
||||
private:
|
||||
static const int MAX_AVG_VARIANCE;
|
||||
static const int MAX_INDIVIDUAL_VARIANCE;
|
||||
|
||||
static std::vector<int> findStartPattern(Ref<BitArray> row);
|
||||
static int decodeCode(Ref<BitArray> row,
|
||||
std::vector<int>& counters,
|
||||
int rowOffset);
|
||||
|
||||
public:
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
Code128Reader();
|
||||
~Code128Reader();
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,14 +19,21 @@
|
|||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
#include <zxing/ChecksumException.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
static const char* ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";
|
||||
using std::vector;
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
using zxing::String;
|
||||
using zxing::NotFoundException;
|
||||
using zxing::ChecksumException;
|
||||
using zxing::oned::Code39Reader;
|
||||
|
||||
namespace {
|
||||
const char* ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";
|
||||
|
||||
/**
|
||||
* These represent the encodings of characters, as patterns of wide and narrow
|
||||
|
@ -35,7 +42,7 @@ namespace oned {
|
|||
* and narrow, with 1s representing "wide" and 0s representing narrow.
|
||||
*/
|
||||
const int CHARACTER_ENCODINGS_LEN = 44;
|
||||
static int CHARACTER_ENCODINGS[CHARACTER_ENCODINGS_LEN] = {
|
||||
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
|
||||
|
@ -43,305 +50,269 @@ namespace oned {
|
|||
0x0A8, 0x0A2, 0x08A, 0x02A // $-%
|
||||
};
|
||||
|
||||
static int ASTERISK_ENCODING = 0x094;
|
||||
static const char* ALPHABET_STRING =
|
||||
int ASTERISK_ENCODING = 0x094;
|
||||
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) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Code39Reader::Code39Reader(bool usingCheckDigit_, bool extendedMode_) :
|
||||
alphabet_string(ALPHABET_STRING),
|
||||
usingCheckDigit(usingCheckDigit_),
|
||||
extendedMode(extendedMode_) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
}
|
||||
Ref<Result> Code39Reader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
||||
vector<int> counters (9, 0);
|
||||
vector<int> start (findAsteriskPattern(row, counters));
|
||||
// Read off white space
|
||||
int nextStart = row->getNextSet(start[1]);
|
||||
int end = row->getSize();
|
||||
|
||||
|
||||
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>();
|
||||
std::string result;
|
||||
char decodedChar;
|
||||
int lastStart;
|
||||
do {
|
||||
recordPattern(row, nextStart, counters);
|
||||
int pattern = toNarrowWidePattern(counters);
|
||||
if (pattern < 0) {
|
||||
throw NotFoundException();;
|
||||
}
|
||||
decodedChar = patternToChar(pattern);
|
||||
result.append(1, decodedChar);
|
||||
lastStart = nextStart;
|
||||
for (int i = 0, end=counters.size(); i < end; i++) {
|
||||
nextStart += counters[i];
|
||||
}
|
||||
// Read off white space
|
||||
|
||||
nextStart = row->getNextSet(nextStart);
|
||||
} while (decodedChar != '*');
|
||||
result.resize(result.length()-1);// remove asterisk
|
||||
|
||||
// Look for whitespace after pattern:
|
||||
int lastPatternSize = 0;
|
||||
for (int i = 0, e = counters.size(); i < e; 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 >> 1) < lastPatternSize) {
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
||||
int* Code39Reader::findAsteriskPattern(Ref<BitArray> row){
|
||||
int width = row->getSize();
|
||||
int rowOffset = 0;
|
||||
while (rowOffset < width) {
|
||||
if (row->get(rowOffset)) {
|
||||
break;
|
||||
}
|
||||
rowOffset++;
|
||||
if (usingCheckDigit) {
|
||||
int max = result.length() - 1;
|
||||
int total = 0;
|
||||
for (int i = 0; i < max; i++) {
|
||||
total += alphabet_string.find_first_of(result[i], 0);
|
||||
}
|
||||
|
||||
int counterPosition = 0;
|
||||
const int countersLen = 9;
|
||||
int counters[countersLen];
|
||||
for (int i = 0; i < countersLen; i++) {
|
||||
counters[i] = 0;
|
||||
if (result[max] != ALPHABET[total % 43]) {
|
||||
throw ChecksumException();
|
||||
}
|
||||
int patternStart = rowOffset;
|
||||
bool isWhite = false;
|
||||
int patternLength = countersLen;
|
||||
result.resize(max);
|
||||
}
|
||||
|
||||
if (result.length() == 0) {
|
||||
// Almost false positive
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
||||
Ref<String> resultString;
|
||||
if (extendedMode) {
|
||||
resultString = decodeExtended(result);
|
||||
} else {
|
||||
resultString = Ref<String>(new String(result));
|
||||
}
|
||||
|
||||
for (int i = rowOffset; i < width; i++) {
|
||||
bool pixel = row->get(i);
|
||||
if (pixel ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
float left = (float) (start[1] + start[0]) / 2.0f;
|
||||
float right = (float) (nextStart + lastStart) / 2.0f;
|
||||
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints (2);
|
||||
resultPoints[0] =
|
||||
Ref<OneDResultPoint>(new OneDResultPoint(left, (float) rowNumber));
|
||||
resultPoints[1] =
|
||||
Ref<OneDResultPoint>(new OneDResultPoint(right, (float) rowNumber));
|
||||
|
||||
return Ref<Result>(
|
||||
new Result(resultString, ArrayRef<char>(), resultPoints, BarcodeFormat::CODE_39)
|
||||
);
|
||||
}
|
||||
|
||||
vector<int> Code39Reader::findAsteriskPattern(Ref<BitArray> row,
|
||||
vector<int>& counters){
|
||||
int width = row->getSize();
|
||||
int rowOffset = row->getNextSet(0);
|
||||
|
||||
int counterPosition = 0;
|
||||
int patternStart = rowOffset;
|
||||
bool isWhite = false;
|
||||
int patternLength = counters.size();
|
||||
|
||||
for (int i = rowOffset; i < width; i++) {
|
||||
if (row->get(i) ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
// Look for whitespace before start pattern, >= 50% of width of
|
||||
// start pattern.
|
||||
if (toNarrowWidePattern(counters) == ASTERISK_ENCODING &&
|
||||
row->isRange(std::max(0, patternStart - ((i - patternStart) >> 1)), patternStart, false)) {
|
||||
vector<int> resultValue (2, 0);
|
||||
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 {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
// Look for whitespace before start pattern, >= 50% of width of
|
||||
// start pattern.
|
||||
if (toNarrowWidePattern(counters, countersLen) == ASTERISK_ENCODING &&
|
||||
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;
|
||||
counterPosition++;
|
||||
}
|
||||
counters[counterPosition] = 1;
|
||||
isWhite = !isWhite;
|
||||
}
|
||||
}
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
||||
// 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(vector<int>& counters){
|
||||
int numCounters = counters.size();
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
maxNarrowCounter = minCounter;
|
||||
wideCounters = 0;
|
||||
int totalWideCountersWidth = 0;
|
||||
int pattern = 0;
|
||||
for (int i = 0; i < numCounters; i++) {
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
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];
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
throw ReaderException("");
|
||||
}
|
||||
} while (wideCounters > 3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
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("");
|
||||
}
|
||||
tmpDecoded.append(1, decodedChar);
|
||||
// bump up i again since we read two characters
|
||||
i++;
|
||||
} else {
|
||||
tmpDecoded.append(1, c);
|
||||
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
|
||||
|
||||
Ref<String> decoded(new String(tmpDecoded));
|
||||
return decoded;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __CODE_39_READER_H__
|
||||
#define __CODE_39_READER_H__
|
||||
/*
|
||||
|
@ -24,35 +25,37 @@
|
|||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
/**
|
||||
* <p>Decodes Code 39 barcodes. This does not support "Full ASCII Code 39" yet.</p>
|
||||
* Ported form Java (author Sean Owen)
|
||||
* @author Lukasz Warchol
|
||||
*/
|
||||
class Code39Reader : public OneDReader {
|
||||
|
||||
private:
|
||||
std::string alphabet_string;
|
||||
|
||||
bool usingCheckDigit;
|
||||
bool extendedMode;
|
||||
|
||||
static int* findAsteriskPattern(Ref<BitArray> row); //throws ReaderException
|
||||
static int toNarrowWidePattern(int counters[], int countersLen);
|
||||
static char patternToChar(int pattern); //throws ReaderException
|
||||
static Ref<String> decodeExtended(std::string encoded); //throws ReaderException
|
||||
|
||||
void append(char* s, char c);
|
||||
public:
|
||||
Code39Reader();
|
||||
Code39Reader(bool usingCheckDigit_);
|
||||
Code39Reader(bool usingCheckDigit_, bool extendedMode_);
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
};
|
||||
}
|
||||
namespace oned {
|
||||
class Code39Reader;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Decodes Code 39 barcodes. This does not support "Full ASCII Code 39" yet.</p>
|
||||
* Ported form Java (author Sean Owen)
|
||||
* @author Lukasz Warchol
|
||||
*/
|
||||
class zxing::oned::Code39Reader : public OneDReader {
|
||||
|
||||
private:
|
||||
std::string alphabet_string;
|
||||
|
||||
bool usingCheckDigit;
|
||||
bool extendedMode;
|
||||
|
||||
static std::vector<int> findAsteriskPattern(Ref<BitArray> row,
|
||||
std::vector<int>& counters);
|
||||
static int toNarrowWidePattern(std::vector<int>& counters);
|
||||
static char patternToChar(int pattern);
|
||||
static Ref<String> decodeExtended(std::string encoded);
|
||||
|
||||
void append(char* s, char c);
|
||||
public:
|
||||
Code39Reader();
|
||||
Code39Reader(bool usingCheckDigit_);
|
||||
Code39Reader(bool usingCheckDigit_, bool extendedMode_);
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
268
cpp/core/src/zxing/oned/Code93Reader.cpp
Normal file
268
cpp/core/src/zxing/oned/Code93Reader.cpp
Normal file
|
@ -0,0 +1,268 @@
|
|||
// -*- 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 "Code93Reader.h"
|
||||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/FormatException.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
#include <zxing/ChecksumException.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
using zxing::String;
|
||||
using zxing::NotFoundException;
|
||||
using zxing::ChecksumException;
|
||||
using zxing::oned::Code93Reader;
|
||||
|
||||
namespace {
|
||||
char const ALPHABET[] =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*";
|
||||
string ALPHABET_STRING (ALPHABET);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
int const CHARACTER_ENCODINGS[] = {
|
||||
0x114, 0x148, 0x144, 0x142, 0x128, 0x124, 0x122, 0x150, 0x112, 0x10A, // 0-9
|
||||
0x1A8, 0x1A4, 0x1A2, 0x194, 0x192, 0x18A, 0x168, 0x164, 0x162, 0x134, // A-J
|
||||
0x11A, 0x158, 0x14C, 0x146, 0x12C, 0x116, 0x1B4, 0x1B2, 0x1AC, 0x1A6, // K-T
|
||||
0x196, 0x19A, 0x16C, 0x166, 0x136, 0x13A, // U-Z
|
||||
0x12E, 0x1D4, 0x1D2, 0x1CA, 0x16E, 0x176, 0x1AE, // - - %
|
||||
0x126, 0x1DA, 0x1D6, 0x132, 0x15E, // Control chars? $-*
|
||||
};
|
||||
int const CHARACTER_ENCODINGS_LENGTH =
|
||||
(int)sizeof(CHARACTER_ENCODINGS)/sizeof(CHARACTER_ENCODINGS[0]);
|
||||
const int ASTERISK_ENCODING = CHARACTER_ENCODINGS[47];
|
||||
}
|
||||
|
||||
Ref<Result> Code93Reader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
||||
Range start (findAsteriskPattern(row));
|
||||
// Read off white space
|
||||
int nextStart = row->getNextSet(start[1]);
|
||||
int end = row->getSize();
|
||||
|
||||
string result;
|
||||
vector<int> counters (6, 0);
|
||||
char decodedChar;
|
||||
int lastStart;
|
||||
do {
|
||||
recordPattern(row, nextStart, counters);
|
||||
int pattern = toPattern(counters);
|
||||
if (pattern < 0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
decodedChar = patternToChar(pattern);
|
||||
result.append(1, decodedChar);
|
||||
lastStart = nextStart;
|
||||
for(int i=0, e=counters.size(); i < e; ++i) {
|
||||
nextStart += counters[i];
|
||||
}
|
||||
// Read off white space
|
||||
nextStart = row->getNextSet(nextStart);
|
||||
} while (decodedChar != '*');
|
||||
result.resize(result.length() - 1); // remove asterisk
|
||||
|
||||
// Should be at least one more black module
|
||||
if (nextStart == end || !row->get(nextStart)) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
if (result.length() < 2) {
|
||||
// false positive -- need at least 2 checksum digits
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
checkChecksums(result);
|
||||
// Remove checksum digits
|
||||
result.resize(result.length() - 2);
|
||||
|
||||
Ref<String> resultString = decodeExtended(result);
|
||||
|
||||
float left = (float) (start[1] + start[0]) / 2.0f;
|
||||
float right = (float) (nextStart + lastStart) / 2.0f;
|
||||
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints (2);
|
||||
resultPoints[0] =
|
||||
Ref<OneDResultPoint>(new OneDResultPoint(left, (float) rowNumber));
|
||||
resultPoints[1] =
|
||||
Ref<OneDResultPoint>(new OneDResultPoint(right, (float) rowNumber));
|
||||
|
||||
return Ref<Result>(new Result(
|
||||
resultString,
|
||||
ArrayRef<char>(),
|
||||
resultPoints,
|
||||
BarcodeFormat::CODE_93));
|
||||
}
|
||||
|
||||
Code93Reader::Range Code93Reader::findAsteriskPattern(Ref<BitArray> row) {
|
||||
int width = row->getSize();
|
||||
int rowOffset = row->getNextSet(0);
|
||||
|
||||
int counterPosition = 0;
|
||||
vector<int> counters (6, 0);
|
||||
int patternStart = rowOffset;
|
||||
bool isWhite = false;
|
||||
int patternLength = counters.size();
|
||||
|
||||
for (int i = rowOffset; i < width; i++) {
|
||||
if (row->get(i) ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
if (toPattern(counters) == ASTERISK_ENCODING) {
|
||||
return Range(patternStart, i);
|
||||
}
|
||||
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 NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
int Code93Reader::toPattern(vector<int>& counters) {
|
||||
int max = counters.size();
|
||||
int sum = 0;
|
||||
for(int i=0, e=counters.size(); i<e; ++i) {
|
||||
sum += counters[i];
|
||||
}
|
||||
int pattern = 0;
|
||||
for (int i = 0; i < max; i++) {
|
||||
int scaledShifted = (counters[i] << INTEGER_MATH_SHIFT) * 9 / sum;
|
||||
int scaledUnshifted = scaledShifted >> INTEGER_MATH_SHIFT;
|
||||
if ((scaledShifted & 0xFF) > 0x7F) {
|
||||
scaledUnshifted++;
|
||||
}
|
||||
if (scaledUnshifted < 1 || scaledUnshifted > 4) {
|
||||
return -1;
|
||||
}
|
||||
if ((i & 0x01) == 0) {
|
||||
for (int j = 0; j < scaledUnshifted; j++) {
|
||||
pattern = (pattern << 1) | 0x01;
|
||||
}
|
||||
} else {
|
||||
pattern <<= scaledUnshifted;
|
||||
}
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
char Code93Reader::patternToChar(int pattern) {
|
||||
for (int i = 0; i < CHARACTER_ENCODINGS_LENGTH; i++) {
|
||||
if (CHARACTER_ENCODINGS[i] == pattern) {
|
||||
return ALPHABET[i];
|
||||
}
|
||||
}
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
Ref<String> Code93Reader::decodeExtended(string const& encoded) {
|
||||
int length = encoded.length();
|
||||
string decoded;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = encoded[i];
|
||||
if (c >= 'a' && c <= 'd') {
|
||||
if (i >= length - 1) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
char next = encoded[i + 1];
|
||||
char decodedChar = '\0';
|
||||
switch (c) {
|
||||
case 'd':
|
||||
// +A to +Z map to a to z
|
||||
if (next >= 'A' && next <= 'Z') {
|
||||
decodedChar = (char) (next + 32);
|
||||
} else {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
// $A to $Z map to control codes SH to SB
|
||||
if (next >= 'A' && next <= 'Z') {
|
||||
decodedChar = (char) (next - 64);
|
||||
} else {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
// %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 FormatException::getFormatInstance();
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
// /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 FormatException::getFormatInstance();
|
||||
}
|
||||
break;
|
||||
}
|
||||
decoded.append(1, decodedChar);
|
||||
// bump up i again since we read two characters
|
||||
i++;
|
||||
} else {
|
||||
decoded.append(1, c);
|
||||
}
|
||||
}
|
||||
return Ref<String>(new String(decoded));
|
||||
}
|
||||
|
||||
void Code93Reader::checkChecksums(string const& result) {
|
||||
int length = result.length();
|
||||
checkOneChecksum(result, length - 2, 20);
|
||||
checkOneChecksum(result, length - 1, 15);
|
||||
}
|
||||
|
||||
void Code93Reader::checkOneChecksum(string const& result,
|
||||
int checkPosition,
|
||||
int weightMax) {
|
||||
int weight = 1;
|
||||
int total = 0;
|
||||
for (int i = checkPosition - 1; i >= 0; i--) {
|
||||
total += weight * ALPHABET_STRING.find_first_of(result[i]);
|
||||
if (++weight > weightMax) {
|
||||
weight = 1;
|
||||
}
|
||||
}
|
||||
if (result[checkPosition] != ALPHABET[total % 47]) {
|
||||
throw ChecksumException::getChecksumInstance();
|
||||
}
|
||||
}
|
53
cpp/core/src/zxing/oned/Code93Reader.h
Normal file
53
cpp/core/src/zxing/oned/Code93Reader.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __CODE_93_READER_H__
|
||||
#define __CODE_93_READER_H__
|
||||
/*
|
||||
* Code93Reader.h
|
||||
* 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/oned/OneDReader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class Code93Reader;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Decodes Code 93 barcodes. This does not support "Full ASCII Code 93" yet.</p>
|
||||
* Ported form Java (author Sean Owen)
|
||||
* @author Lukasz Warchol
|
||||
*/
|
||||
class zxing::oned::Code93Reader : public OneDReader {
|
||||
public:
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
|
||||
private:
|
||||
static Range findAsteriskPattern(Ref<BitArray> row);
|
||||
static int toPattern(std::vector<int>& counters);
|
||||
static char patternToChar(int pattern);
|
||||
static Ref<String> decodeExtended(std::string const& encoded);
|
||||
static void checkChecksums(std::string const& result);
|
||||
static void checkOneChecksum(std::string const& result,
|
||||
int checkPosition,
|
||||
int weightMax);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -16,79 +16,76 @@
|
|||
*/
|
||||
|
||||
#include "EAN13Reader.h"
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
using std::vector;
|
||||
using zxing::Ref;
|
||||
using zxing::BitArray;
|
||||
using zxing::oned::EAN13Reader;
|
||||
|
||||
static const int FIRST_DIGIT_ENCODINGS[10] = {
|
||||
0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A
|
||||
};
|
||||
namespace {
|
||||
const int FIRST_DIGIT_ENCODINGS[10] = {
|
||||
0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A
|
||||
};
|
||||
}
|
||||
|
||||
EAN13Reader::EAN13Reader() { }
|
||||
EAN13Reader::EAN13Reader() : decodeMiddleCounters(4, 0) { }
|
||||
|
||||
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 EAN13Reader::decodeMiddle(Ref<BitArray> row,
|
||||
Range const& startRange,
|
||||
std::string& resultString) {
|
||||
if (false) {
|
||||
std::cerr << "ba "
|
||||
<< startRange[0] << " "
|
||||
<< startRange[1] << " "
|
||||
<< *row << std::endl;
|
||||
}
|
||||
vector<int>& counters (decodeMiddleCounters);
|
||||
counters.clear();
|
||||
counters.resize(4);
|
||||
int end = row->getSize();
|
||||
int rowOffset = startRange[1];
|
||||
|
||||
int end = row->getSize();
|
||||
int rowOffset = startGuardEnd;
|
||||
int lgPatternFound = 0;
|
||||
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;
|
||||
for (int x = 0; x < 6 && rowOffset < end; x++) {
|
||||
int bestMatch = decodeDigit(row, counters, rowOffset, L_AND_G_PATTERNS);
|
||||
resultString.append(1, (char) ('0' + bestMatch % 10));
|
||||
for (int i = 0, end = counters.size(); i <end; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
|
||||
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;
|
||||
if (bestMatch >= 10) {
|
||||
lgPatternFound |= 1 << (5 - x);
|
||||
}
|
||||
}
|
||||
|
||||
determineFirstDigit(resultString, lgPatternFound);
|
||||
|
||||
Range middleRange = findGuardPattern(row, rowOffset, true, MIDDLE_PATTERN) ;
|
||||
rowOffset = middleRange[1];
|
||||
|
||||
for (int x = 0; x < 6 && rowOffset < end; x++) {
|
||||
int bestMatch =
|
||||
decodeDigit(row, counters, rowOffset, L_PATTERNS);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
for (int i = 0, end = counters.size(); i < end; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
}
|
||||
return rowOffset;
|
||||
}
|
||||
|
||||
void EAN13Reader::determineFirstDigit(std::string& resultString, int lgPatternFound) {
|
||||
// std::cerr << "K " << resultString << " " << lgPatternFound << " " <<FIRST_DIGIT_ENCODINGS << std::endl;
|
||||
for (int d = 0; d < 10; d++) {
|
||||
if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) {
|
||||
resultString.insert(0, 1, (char) ('0' + d));
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
zxing::BarcodeFormat EAN13Reader::getBarcodeFormat(){
|
||||
return BarcodeFormat::EAN_13;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __EAN_13_READER_H__
|
||||
#define __EAN_13_READER_H__
|
||||
|
||||
|
@ -25,20 +26,24 @@
|
|||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class EAN13Reader : public UPCEANReader {
|
||||
|
||||
private:
|
||||
static bool determineFirstDigit(std::string& resultString, int lgPatternFound);
|
||||
|
||||
public:
|
||||
EAN13Reader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, int startGuardBegin, int startGuardEnd,
|
||||
std::string& resultString);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
};
|
||||
class EAN13Reader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::EAN13Reader : public UPCEANReader {
|
||||
private:
|
||||
std::vector<int> decodeMiddleCounters;
|
||||
static void determineFirstDigit(std::string& resultString,
|
||||
int lgPatternFound);
|
||||
|
||||
public:
|
||||
EAN13Reader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row,
|
||||
Range const& startRange,
|
||||
std::string& resultString);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,55 +18,44 @@
|
|||
#include "EAN8Reader.h"
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
using std::vector;
|
||||
using zxing::oned::EAN8Reader;
|
||||
|
||||
EAN8Reader::EAN8Reader(){ }
|
||||
EAN8Reader::EAN8Reader() : decodeMiddleCounters(4, 0) { }
|
||||
|
||||
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 EAN8Reader::decodeMiddle(Ref<BitArray> row,
|
||||
Range const& startRange,
|
||||
std::string& result){
|
||||
vector<int>& counters (decodeMiddleCounters);
|
||||
counters[0] = 0;
|
||||
counters[1] = 0;
|
||||
counters[2] = 0;
|
||||
counters[3] = 0;
|
||||
|
||||
int end = row->getSize();
|
||||
int rowOffset = startGuardEnd;
|
||||
int end = row->getSize();
|
||||
int rowOffset = startRange[1];
|
||||
|
||||
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;
|
||||
for (int x = 0; x < 4 && rowOffset < end; x++) {
|
||||
int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS);
|
||||
result.append(1, (char) ('0' + bestMatch));
|
||||
for (int i = 0, end = counters.size(); i < end; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
}
|
||||
|
||||
Range middleRange =
|
||||
findGuardPattern(row, rowOffset, true, MIDDLE_PATTERN);
|
||||
rowOffset = middleRange[1];
|
||||
for (int x = 0; x < 4 && rowOffset < end; x++) {
|
||||
int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS);
|
||||
result.append(1, (char) ('0' + bestMatch));
|
||||
for (int i = 0, end = counters.size(); i < end; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
}
|
||||
return rowOffset;
|
||||
}
|
||||
|
||||
zxing::BarcodeFormat EAN8Reader::getBarcodeFormat(){
|
||||
return BarcodeFormat::EAN_8;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __EAN_8_READER_H__
|
||||
#define __EAN_8_READER_H__
|
||||
|
||||
|
@ -25,17 +26,21 @@
|
|||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class EAN8Reader : public UPCEANReader {
|
||||
|
||||
public:
|
||||
EAN8Reader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, int startGuardBegin, int startGuardEnd,
|
||||
std::string& resultString);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
};
|
||||
class EAN8Reader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::EAN8Reader : public UPCEANReader {
|
||||
std::vector<int> decodeMiddleCounters;
|
||||
|
||||
public:
|
||||
EAN8Reader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row,
|
||||
Range const& startRange,
|
||||
std::string& resultString);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,227 +19,216 @@
|
|||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/FormatException.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
using std::vector;
|
||||
using zxing::Ref;
|
||||
using zxing::ArrayRef;
|
||||
using zxing::Array;
|
||||
using zxing::Result;
|
||||
using zxing::FormatException;
|
||||
using zxing::NotFoundException;
|
||||
using zxing::oned::ITFReader;
|
||||
|
||||
static const int W = 3; // Pixel width of a wide line
|
||||
static const int N = 1; // Pixed width of a narrow line
|
||||
#define VECTOR_INIT(v) v, v + sizeof(v)/sizeof(v[0])
|
||||
|
||||
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 };
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* 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};
|
||||
const int W = 3; // Pixel width of a wide line
|
||||
const int N = 1; // Pixed width of a narrow line
|
||||
|
||||
static const int END_PATTERN_REVERSED_LEN = 3;
|
||||
static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W};
|
||||
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 };
|
||||
const ArrayRef<int> DEFAULT_ALLOWED_LENGTHS (new Array<int>(DEFAULT_ALLOWED_LENGTHS_,
|
||||
DEFAULT_ALLOWED_LENGTHS_LEN ));
|
||||
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
/**
|
||||
* Start/end guard pattern.
|
||||
*
|
||||
* Note: The end pattern is reversed because the row is reversed before
|
||||
* searching for the END_PATTERN
|
||||
*/
|
||||
const int START_PATTERN_LEN = 4;
|
||||
const int START_PATTERN_[START_PATTERN_LEN] = {N, N, N, N};
|
||||
const vector<int> START_PATTERN (VECTOR_INIT(START_PATTERN_));
|
||||
|
||||
const int END_PATTERN_REVERSED_LEN = 3;
|
||||
const int END_PATTERN_REVERSED_[END_PATTERN_REVERSED_LEN] = {N, N, W};
|
||||
const vector<int> END_PATTERN_REVERSED (VECTOR_INIT(END_PATTERN_REVERSED_));
|
||||
|
||||
/**
|
||||
* Patterns of Wide / Narrow lines to indicate each digit
|
||||
*/
|
||||
const int PATTERNS_LEN = 10;
|
||||
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) {
|
||||
}
|
||||
|
||||
|
||||
ITFReader::ITFReader() : narrowLineWidth(-1) {
|
||||
Ref<Result> ITFReader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
||||
// Find out where the Middle section (payload) starts & ends
|
||||
|
||||
Range startRange = decodeStart(row);
|
||||
Range endRange = decodeEnd(row);
|
||||
|
||||
std::string result;
|
||||
decodeMiddle(row, startRange[1], endRange[0], result);
|
||||
Ref<String> resultString(new String(result));
|
||||
|
||||
ArrayRef<int> allowedLengths;
|
||||
// Java hints stuff missing
|
||||
if (!allowedLengths) {
|
||||
allowedLengths = DEFAULT_ALLOWED_LENGTHS;
|
||||
}
|
||||
|
||||
// To avoid false positives with 2D barcodes (and other patterns), make
|
||||
// an assumption that the decoded string must be 6, 10 or 14 digits.
|
||||
int length = resultString->size();
|
||||
bool lengthOK = false;
|
||||
for (int i = 0, e = allowedLengths->size(); i < e; i++) {
|
||||
if (length == allowedLengths[i]) {
|
||||
lengthOK = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!lengthOK) {
|
||||
throw FormatException();
|
||||
}
|
||||
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints(2);
|
||||
resultPoints[0] = Ref<OneDResultPoint>(new OneDResultPoint(startRange[1], (float) rowNumber));
|
||||
resultPoints[1] = Ref<OneDResultPoint>(new OneDResultPoint(endRange[0], (float) rowNumber));
|
||||
return Ref<Result>(new Result(resultString, ArrayRef<char>(), resultPoints, BarcodeFormat::ITF));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
vector<int> counterDigitPair(10, 0);
|
||||
vector<int> counterBlack(5, 0);
|
||||
vector<int> counterWhite(5, 0);
|
||||
|
||||
while (payloadStart < payloadEnd) {
|
||||
|
||||
// Get 10 runs of black/white.
|
||||
recordPattern(row, payloadStart, counterDigitPair);
|
||||
// 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);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
bestMatch = decodeDigit(counterWhite);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
|
||||
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>();
|
||||
}
|
||||
for (int i = 0, e = counterDigitPair.size(); i < e; i++) {
|
||||
payloadStart += counterDigitPair[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
ITFReader::Range ITFReader::decodeStart(Ref<BitArray> row) {
|
||||
int endStart = skipWhiteSpace(row);
|
||||
Range startPattern = findGuardPattern(row, endStart, START_PATTERN);
|
||||
|
||||
int counterBlack[5];
|
||||
int counterWhite[5];
|
||||
for (int i=0; i<5; i++) {
|
||||
counterBlack[i] = 0;
|
||||
counterWhite[i] = 0;
|
||||
}
|
||||
// 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;
|
||||
|
||||
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];
|
||||
}
|
||||
validateQuietZone(row, startPattern[0]);
|
||||
return startPattern;
|
||||
}
|
||||
|
||||
int bestMatch = decodeDigit(counterBlack, 5);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
bestMatch = decodeDigit(counterWhite, 5);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
for (int i = 0; i < counterDigitPairLen; i++) {
|
||||
payloadStart += counterDigitPair[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
ITFReader::Range ITFReader::decodeEnd(Ref<BitArray> row) {
|
||||
// For convenience, reverse the row and then
|
||||
// search from 'the start' for the end block
|
||||
BitArray::Reverse r (row);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
int endStart = skipWhiteSpace(row);
|
||||
Range endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// 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]);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
// 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;
|
||||
|
||||
return endPattern;
|
||||
}
|
||||
|
||||
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;
|
||||
/**
|
||||
* 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
|
||||
//
|
||||
|
@ -253,115 +242,102 @@ namespace zxing {
|
|||
// // 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
ITFReader::Range ITFReader::findGuardPattern(Ref<BitArray> row,
|
||||
int rowOffset,
|
||||
vector<int> const& pattern) {
|
||||
// TODO: This is very similar to implementation in UPCEANReader. Consider if they can be
|
||||
// merged to a single method.
|
||||
int patternLength = pattern.size();
|
||||
vector<int> counters(patternLength, 0);
|
||||
int width = row->getSize();
|
||||
bool isWhite = false;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
int counterPosition = 0;
|
||||
int patternStart = rowOffset;
|
||||
for (int x = rowOffset; x < width; x++) {
|
||||
if (row->get(x) ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
if (patternMatchVariance(counters, &pattern[0], MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
|
||||
return Range(patternStart, x);
|
||||
}
|
||||
}
|
||||
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];
|
||||
patternStart += counters[0] + counters[1];
|
||||
for (int y = 2; y < patternLength; y++) {
|
||||
counters[y - 2] = counters[y];
|
||||
}
|
||||
unsigned int variance = patternMatchVariance(counters, countersLen, pattern,
|
||||
MAX_INDIVIDUAL_VARIANCE);
|
||||
if (variance < bestVariance) {
|
||||
bestVariance = variance;
|
||||
bestMatch = i;
|
||||
}
|
||||
}
|
||||
if (bestMatch >= 0) {
|
||||
return bestMatch;
|
||||
counters[patternLength - 2] = 0;
|
||||
counters[patternLength - 1] = 0;
|
||||
counterPosition--;
|
||||
} else {
|
||||
throw ReaderException("digit didint found");
|
||||
counterPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
ITFReader::~ITFReader(){
|
||||
counters[counterPosition] = 1;
|
||||
isWhite = !isWhite;
|
||||
}
|
||||
}
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(vector<int>& counters){
|
||||
|
||||
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 const* pattern = PATTERNS[i];
|
||||
int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
|
||||
if (variance < bestVariance) {
|
||||
bestVariance = variance;
|
||||
bestMatch = i;
|
||||
}
|
||||
}
|
||||
if (bestMatch >= 0) {
|
||||
return bestMatch;
|
||||
} else {
|
||||
throw NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
ITFReader::~ITFReader(){}
|
||||
|
|
|
@ -24,30 +24,32 @@
|
|||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class ITFReader : public OneDReader {
|
||||
|
||||
private:
|
||||
enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 420/1000)};
|
||||
enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 800/1000)};
|
||||
// Stores the actual narrow line width of the image being decoded.
|
||||
int narrowLineWidth;
|
||||
|
||||
int* decodeStart(Ref<BitArray> row); //throws ReaderException
|
||||
int* decodeEnd(Ref<BitArray> row); //throws ReaderException
|
||||
static void decodeMiddle(Ref<BitArray> row, int payloadStart, int payloadEnd, std::string& resultString); //throws ReaderException
|
||||
void validateQuietZone(Ref<BitArray> row, int startPattern); //throws ReaderException
|
||||
static int skipWhiteSpace(Ref<BitArray> row); //throws ReaderException
|
||||
|
||||
static int* findGuardPattern(Ref<BitArray> row, int rowOffset, const int pattern[], int patternLen); //throws ReaderException
|
||||
static int decodeDigit(int counters[], int countersLen); //throws ReaderException
|
||||
|
||||
void append(char* s, char c);
|
||||
public:
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row); ///throws ReaderException
|
||||
ITFReader();
|
||||
~ITFReader();
|
||||
};
|
||||
}
|
||||
class ITFReader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::ITFReader : public OneDReader {
|
||||
|
||||
private:
|
||||
enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 420/1000)};
|
||||
enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 800/1000)};
|
||||
// Stores the actual narrow line width of the image being decoded.
|
||||
int narrowLineWidth;
|
||||
|
||||
Range decodeStart(Ref<BitArray> row);
|
||||
Range decodeEnd(Ref<BitArray> row);
|
||||
static void decodeMiddle(Ref<BitArray> row, int payloadStart, int payloadEnd, std::string& resultString);
|
||||
void validateQuietZone(Ref<BitArray> row, int startPattern);
|
||||
static int skipWhiteSpace(Ref<BitArray> row);
|
||||
|
||||
static Range findGuardPattern(Ref<BitArray> row, int rowOffset, std::vector<int> const& pattern);
|
||||
static int decodeDigit(std::vector<int>& counters);
|
||||
|
||||
void append(char* s, char c);
|
||||
public:
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
ITFReader();
|
||||
~ITFReader();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* MultiFormatOneDReader.cpp
|
||||
* ZXing
|
||||
|
@ -22,45 +23,76 @@
|
|||
#include <zxing/oned/MultiFormatUPCEANReader.h>
|
||||
#include <zxing/oned/Code39Reader.h>
|
||||
#include <zxing/oned/Code128Reader.h>
|
||||
#include <zxing/oned/Code93Reader.h>
|
||||
#include <zxing/oned/CodaBarReader.h>
|
||||
#include <zxing/oned/ITFReader.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/NotFoundException.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()));
|
||||
}
|
||||
}
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
using zxing::oned::MultiFormatOneDReader;
|
||||
|
||||
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>();
|
||||
}
|
||||
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_93)) {
|
||||
readers.push_back(Ref<OneDReader>(new Code93Reader()));
|
||||
}
|
||||
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 (hints.containsFormat(BarcodeFormat::CODABAR)) {
|
||||
readers.push_back(Ref<OneDReader>(new CodaBarReader()));
|
||||
}
|
||||
/*
|
||||
if (hints.containsFormat(BarcodeFormat::RSS_14)) {
|
||||
readers.push_back(Ref<OneDReader>(new RSS14Reader()));
|
||||
}
|
||||
*/
|
||||
/*
|
||||
if (hints.containsFormat(BarcodeFormat::RSS_EXPANDED)) {
|
||||
readers.push_back(Ref<OneDReader>(new RSS14ExpandedReader()));
|
||||
}
|
||||
*/
|
||||
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 CodaBarReader()));
|
||||
readers.push_back(Ref<OneDReader>(new Code93Reader()));
|
||||
readers.push_back(Ref<OneDReader>(new Code128Reader()));
|
||||
readers.push_back(Ref<OneDReader>(new ITFReader()));
|
||||
// readers.push_back(Ref<OneDReader>(new RSS14Reader()));
|
||||
// readers.push_back(Ref<OneDReader>(new RSS14ExpandedReader()));
|
||||
}
|
||||
}
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
Ref<Result> MultiFormatOneDReader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
||||
int size = readers.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
OneDReader* reader = readers[i];
|
||||
try {
|
||||
// std::cerr << "v 1 " << typeid(*reader).name() << " " << rowNumber << std::endl;
|
||||
Ref<Result> result = reader->decodeRow(rowNumber, row);
|
||||
// std::cerr << "^ 1 " << typeid(*reader).name() << " " << rowNumber << std::endl;
|
||||
return result;
|
||||
} catch (ReaderException const& re) {
|
||||
// std::cerr << "^ * " << typeid(*reader).name() << " " << rowNumber << std::endl;
|
||||
// continue
|
||||
}
|
||||
}
|
||||
// std::cerr << "throwing nfe" << std::endl;
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* MultiFormatUPCEANReader.cpp
|
||||
* ZXing
|
||||
|
@ -25,63 +26,81 @@
|
|||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/NotFoundException.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>();
|
||||
}
|
||||
using zxing::NotFoundException;
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
using zxing::oned::MultiFormatUPCEANReader;
|
||||
|
||||
MultiFormatUPCEANReader::MultiFormatUPCEANReader(DecodeHints hints) : readers() {
|
||||
if (hints.containsFormat(BarcodeFormat::EAN_13)) {
|
||||
readers.push_back(Ref<UPCEANReader>(new EAN13Reader()));
|
||||
} else if (hints.containsFormat(BarcodeFormat::UPC_A)) {
|
||||
readers.push_back(Ref<UPCEANReader>(new UPCAReader()));
|
||||
}
|
||||
if (hints.containsFormat(BarcodeFormat::EAN_8)) {
|
||||
readers.push_back(Ref<UPCEANReader>(new EAN8Reader()));
|
||||
}
|
||||
if (hints.containsFormat(BarcodeFormat::UPC_E)) {
|
||||
readers.push_back(Ref<UPCEANReader>(new UPCEReader()));
|
||||
}
|
||||
if (readers.size() == 0) {
|
||||
readers.push_back(Ref<UPCEANReader>(new EAN13Reader()));
|
||||
// UPC-A is covered by EAN-13
|
||||
readers.push_back(Ref<UPCEANReader>(new EAN8Reader()));
|
||||
readers.push_back(Ref<UPCEANReader>(new UPCEReader()));
|
||||
}
|
||||
}
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
Ref<Result> MultiFormatUPCEANReader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
||||
// Compute this location once and reuse it on multiple implementations
|
||||
UPCEANReader::Range startGuardPattern = UPCEANReader::findStartGuardPattern(row);
|
||||
// std::cerr << "sgp " << startGuardPattern[0] << " " << startGuardPattern[1] << std::endl;
|
||||
for (int i = 0, e = readers.size(); i < e; i++) {
|
||||
Ref<UPCEANReader> reader = readers[i];
|
||||
Ref<Result> result;
|
||||
try {
|
||||
// std::cerr << typeid(*reader).name() << " " << rowNumber << std::endl;
|
||||
result = reader->decodeRow(rowNumber, row, startGuardPattern);
|
||||
} catch (ReaderException const& re) {
|
||||
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.
|
||||
bool ean13MayBeUPCA =
|
||||
result->getBarcodeFormat() == BarcodeFormat::EAN_13 &&
|
||||
result->getText()->charAt(0) == '0';
|
||||
|
||||
// Note: doesn't match Java which uses hints
|
||||
|
||||
bool canReturnUPCA = true;
|
||||
|
||||
if (ean13MayBeUPCA && canReturnUPCA) {
|
||||
// Transfer the metdata across
|
||||
Ref<Result> resultUPCA (new Result(result->getText()->substring(1),
|
||||
result->getRawBytes(),
|
||||
result->getResultPoints(),
|
||||
BarcodeFormat::UPC_A));
|
||||
// needs java metadata stuff
|
||||
return resultUPCA;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __MULTI_FORMAT_UPC_EAN_READER_H__
|
||||
#define __MULTI_FORMAT_UPC_EAN_READER_H__
|
||||
/*
|
||||
|
@ -23,16 +24,17 @@
|
|||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class MultiFormatUPCEANReader : public OneDReader {
|
||||
|
||||
private:
|
||||
std::vector<Ref<OneDReader> > readers;
|
||||
public:
|
||||
MultiFormatUPCEANReader(DecodeHints hints);
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
};
|
||||
class UPCEANReader;
|
||||
class MultiFormatUPCEANReader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::MultiFormatUPCEANReader : public OneDReader {
|
||||
private:
|
||||
std::vector< Ref<UPCEANReader> > readers;
|
||||
public:
|
||||
MultiFormatUPCEANReader(DecodeHints hints);
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,187 +21,202 @@
|
|||
#include "OneDReader.h"
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
using namespace std;
|
||||
using std::vector;
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
using zxing::NotFoundException;
|
||||
using zxing::oned::OneDReader;
|
||||
|
||||
OneDReader::OneDReader() {
|
||||
}
|
||||
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()));
|
||||
}
|
||||
Ref<Result> OneDReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
||||
try {
|
||||
return doDecode(image, hints);
|
||||
} catch (NotFoundException const& nfe) {
|
||||
// std::cerr << "trying harder" << std::endl;
|
||||
bool tryHarder = hints.getTryHarder();
|
||||
if (tryHarder && image->isRotateSupported()) {
|
||||
// std::cerr << "v rotate" << std::endl;
|
||||
Ref<BinaryBitmap> rotatedImage(image->rotateCounterClockwise());
|
||||
// std::cerr << "^ rotate" << std::endl;
|
||||
Ref<Result> result = doDecode(rotatedImage, hints);
|
||||
// Doesn't have java metadata stuff
|
||||
ArrayRef< Ref<ResultPoint> >& points (result->getResultPoints());
|
||||
if (points && !points->empty()) {
|
||||
int height = rotatedImage->getHeight();
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
points[i].reset(new OneDResultPoint(height - points[i]->getY() - 1, points[i]->getX()));
|
||||
}
|
||||
}
|
||||
if (result.empty()) {
|
||||
throw ReaderException("");
|
||||
}
|
||||
// std::cerr << "tried harder" << std::endl;
|
||||
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() {
|
||||
} else {
|
||||
// std::cerr << "tried harder nfe" << std::endl;
|
||||
throw nfe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
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 = std::max(1, height >> (tryHarder ? 8 : 5));
|
||||
using namespace std;
|
||||
// cerr << "rS " << rowStep << " " << height << " " << tryHarder << endl;
|
||||
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 (false) {
|
||||
std::cerr << "rN "
|
||||
<< rowNumber << " "
|
||||
<< height << " "
|
||||
<< middle << " "
|
||||
<< rowStep << " "
|
||||
<< isAbove << " "
|
||||
<< rowStepsAboveOrBelow
|
||||
<< std::endl;
|
||||
}
|
||||
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 (NotFoundException const& nfe) {
|
||||
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
|
||||
}
|
||||
|
||||
// Java hints stuff missing
|
||||
|
||||
try {
|
||||
// Look for a barcode
|
||||
// std::cerr << "rn " << rowNumber << " " << typeid(*this).name() << std::endl;
|
||||
Ref<Result> result = decodeRow(rowNumber, row);
|
||||
// We found our barcode
|
||||
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.
|
||||
ArrayRef< Ref<ResultPoint> > points(result->getResultPoints());
|
||||
if (points) {
|
||||
points[0] = Ref<ResultPoint>(new OneDResultPoint(width - points[0]->getX() - 1,
|
||||
points[0]->getY()));
|
||||
points[1] = Ref<ResultPoint>(new OneDResultPoint(width - points[1]->getX() - 1,
|
||||
points[1]->getY()));
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (ReaderException const& re) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
||||
int OneDReader::patternMatchVariance(vector<int>& counters,
|
||||
vector<int> const& pattern,
|
||||
int maxIndividualVariance) {
|
||||
return patternMatchVariance(counters, &pattern[0], maxIndividualVariance);
|
||||
}
|
||||
|
||||
int OneDReader::patternMatchVariance(vector<int>& counters,
|
||||
int const pattern[],
|
||||
int maxIndividualVariance) {
|
||||
int numCounters = counters.size();
|
||||
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"
|
||||
int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
|
||||
maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void OneDReader::recordPattern(Ref<BitArray> row,
|
||||
int start,
|
||||
vector<int>& counters) {
|
||||
int numCounters = counters.size();
|
||||
for (int i = 0; i < numCounters; i++) {
|
||||
counters[i] = 0;
|
||||
}
|
||||
int end = row->getSize();
|
||||
if (start >= end) {
|
||||
throw NotFoundException();
|
||||
}
|
||||
bool isWhite = !row->get(start);
|
||||
int counterPosition = 0;
|
||||
int i = start;
|
||||
while (i < end) {
|
||||
if (row->get(i) ^ isWhite) { // that is, exactly one is true
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
counterPosition++;
|
||||
if (counterPosition == numCounters) {
|
||||
break;
|
||||
} else {
|
||||
counters[counterPosition] = 1;
|
||||
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))) {
|
||||
throw NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
OneDReader::~OneDReader() {}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __ONED_READER_H__
|
||||
#define __ONED_READER_H__
|
||||
|
||||
|
@ -23,28 +24,58 @@
|
|||
#include <zxing/Reader.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class OneDReader : public Reader {
|
||||
private:
|
||||
static const int INTEGER_MATH_SHIFT = 8;
|
||||
|
||||
Ref<Result> doDecode(Ref<BinaryBitmap> image, DecodeHints hints);
|
||||
public:
|
||||
static const int PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT;
|
||||
|
||||
OneDReader();
|
||||
virtual Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints);
|
||||
|
||||
// Implementations must not throw any exceptions. If a barcode is not found on this row,
|
||||
// a empty ref should be returned e.g. return Ref<Result>();
|
||||
virtual Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row) = 0;
|
||||
|
||||
static unsigned int patternMatchVariance(int counters[], int countersSize,
|
||||
const int pattern[], int maxIndividualVariance);
|
||||
static bool recordPattern(Ref<BitArray> row, int start, int counters[], int countersCount);
|
||||
virtual ~OneDReader();
|
||||
};
|
||||
}
|
||||
namespace oned {
|
||||
class OneDReader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::OneDReader : public Reader {
|
||||
private:
|
||||
Ref<Result> doDecode(Ref<BinaryBitmap> image, DecodeHints hints);
|
||||
|
||||
protected:
|
||||
static const int INTEGER_MATH_SHIFT = 8;
|
||||
|
||||
struct Range {
|
||||
private:
|
||||
int data[2];
|
||||
public:
|
||||
Range() {}
|
||||
Range(int zero, int one) {
|
||||
data[0] = zero;
|
||||
data[1] = one;
|
||||
}
|
||||
int& operator [] (int index) {
|
||||
return data[index];
|
||||
}
|
||||
int const& operator [] (int index) const {
|
||||
return data[index];
|
||||
}
|
||||
};
|
||||
|
||||
static int patternMatchVariance(std::vector<int>& counters,
|
||||
std::vector<int> const& pattern,
|
||||
int maxIndividualVariance);
|
||||
static int patternMatchVariance(std::vector<int>& counters,
|
||||
int const pattern[],
|
||||
int maxIndividualVariance);
|
||||
|
||||
protected:
|
||||
static const int PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT;
|
||||
|
||||
public:
|
||||
|
||||
OneDReader();
|
||||
virtual Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints);
|
||||
|
||||
// Implementations must not throw any exceptions. If a barcode is not found on this row,
|
||||
// a empty ref should be returned e.g. return Ref<Result>();
|
||||
virtual Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row) = 0;
|
||||
|
||||
static void recordPattern(Ref<BitArray> row,
|
||||
int start,
|
||||
std::vector<int>& counters);
|
||||
virtual ~OneDReader();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* UPCAReader.cpp
|
||||
* ZXing
|
||||
|
@ -20,46 +21,47 @@
|
|||
#include "UPCAReader.h"
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
UPCAReader::UPCAReader() : ean13Reader() {
|
||||
}
|
||||
using zxing::oned::UPCAReader;
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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,
|
||||
Range const& startGuardRange) {
|
||||
return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardRange));
|
||||
}
|
||||
|
||||
Ref<Result> UPCAReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
||||
return maybeReturnResult(ean13Reader.decode(image, hints));
|
||||
}
|
||||
|
||||
int UPCAReader::decodeMiddle(Ref<BitArray> row,
|
||||
Range const& startRange,
|
||||
std::string& resultString) {
|
||||
return ean13Reader.decodeMiddle(row, startRange, 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>();
|
||||
}
|
||||
|
||||
zxing::BarcodeFormat UPCAReader::getBarcodeFormat(){
|
||||
return BarcodeFormat::UPC_A;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __UPCA_READER_H__
|
||||
#define __UPCA_READER_H__
|
||||
/*
|
||||
|
@ -24,26 +25,26 @@
|
|||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class UPCAReader : public UPCEANReader {
|
||||
|
||||
private:
|
||||
EAN13Reader ean13Reader;
|
||||
static Ref<Result> maybeReturnResult(Ref<Result> result);
|
||||
|
||||
public:
|
||||
UPCAReader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, int startGuardBegin, int startGuardEnd,
|
||||
std::string& resultString);
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row, int startGuardBegin,
|
||||
int startGuardEnd);
|
||||
Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
};
|
||||
class UPCAReader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::UPCAReader : public UPCEANReader {
|
||||
|
||||
private:
|
||||
EAN13Reader ean13Reader;
|
||||
static Ref<Result> maybeReturnResult(Ref<Result> result);
|
||||
|
||||
public:
|
||||
UPCAReader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, Range const& startRange, std::string& resultString);
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row, Range const& startGuardRange);
|
||||
Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
/*
|
||||
* UPCEANReader.cpp
|
||||
* ZXing
|
||||
|
@ -20,292 +21,280 @@
|
|||
#include "UPCEANReader.h"
|
||||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <zxing/NotFoundException.h>
|
||||
#include <zxing/ChecksumException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
using zxing::Ref;
|
||||
using zxing::Result;
|
||||
using zxing::NotFoundException;
|
||||
using zxing::ChecksumException;
|
||||
using zxing::oned::UPCEANReader;
|
||||
using namespace std; // remove
|
||||
|
||||
/**
|
||||
* Start/end guard pattern.
|
||||
*/
|
||||
static const int START_END_PATTERN[3] = {1, 1, 1};
|
||||
#define LEN(v) ((int)(sizeof(v)/sizeof(v[0])))
|
||||
|
||||
/**
|
||||
* 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};
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* "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
|
||||
};
|
||||
/**
|
||||
* Start/end guard pattern.
|
||||
*/
|
||||
const int START_END_PATTERN_[] = {1, 1, 1};
|
||||
const int START_END_PATTERN_LEN = LEN(START_END_PATTERN_);
|
||||
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
/**
|
||||
* Pattern marking the middle of a UPC/EAN pattern, separating the two halves.
|
||||
*/
|
||||
const int MIDDLE_PATTERN_[] = {1, 1, 1, 1, 1};
|
||||
const int MIDDLE_PATTERN_LEN = LEN(MIDDLE_PATTERN_);
|
||||
|
||||
/**
|
||||
* "Odd", or "L" patterns used to encode UPC/EAN digits.
|
||||
*/
|
||||
const int L_PATTERNS_[][4] = {
|
||||
{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
|
||||
};
|
||||
const int L_PATTERNS_LEN = LEN(L_PATTERNS_);
|
||||
|
||||
int UPCEANReader::getMIDDLE_PATTERN_LEN() {
|
||||
return MIDDLE_PATTERN_LEN;
|
||||
/**
|
||||
* As above but also including the "even", or "G" patterns used to encode UPC/EAN digits.
|
||||
*/
|
||||
const int L_AND_G_PATTERNS_[][4] = {
|
||||
{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
|
||||
};
|
||||
const int L_AND_G_PATTERNS_LEN = LEN(L_AND_G_PATTERNS_);
|
||||
}
|
||||
|
||||
const int UPCEANReader::MAX_AVG_VARIANCE = (int)(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.48f);
|
||||
const int UPCEANReader::MAX_INDIVIDUAL_VARIANCE = (int)(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f);
|
||||
|
||||
#define VECTOR_INIT(v) v, v + sizeof(v)/sizeof(v[0])
|
||||
|
||||
const vector<int>
|
||||
UPCEANReader::START_END_PATTERN (VECTOR_INIT(START_END_PATTERN_));
|
||||
|
||||
const vector<int>
|
||||
UPCEANReader::MIDDLE_PATTERN (VECTOR_INIT(MIDDLE_PATTERN_));
|
||||
const vector<int const*>
|
||||
UPCEANReader::L_PATTERNS (VECTOR_INIT(L_PATTERNS_));
|
||||
const vector<int const*>
|
||||
UPCEANReader::L_AND_G_PATTERNS (VECTOR_INIT(L_AND_G_PATTERNS_));
|
||||
|
||||
UPCEANReader::UPCEANReader() {}
|
||||
|
||||
Ref<Result> UPCEANReader::decodeRow(int rowNumber, Ref<BitArray> row) {
|
||||
return decodeRow(rowNumber, row, findStartGuardPattern(row));
|
||||
}
|
||||
|
||||
Ref<Result> UPCEANReader::decodeRow(int rowNumber,
|
||||
Ref<BitArray> row,
|
||||
Range const& startGuardRange) {
|
||||
if (false) {
|
||||
std::cerr << "dR " << rowNumber << " " << *row << " "
|
||||
<< startGuardRange[0] << " " << startGuardRange[1] << std::endl;
|
||||
}
|
||||
string& result = decodeRowStringBuffer;
|
||||
result.clear();
|
||||
// cerr << "drx " << rowNumber << endl;
|
||||
int endStart = decodeMiddle(row, startGuardRange, result);
|
||||
|
||||
Range endRange = decodeEnd(row, endStart);
|
||||
|
||||
// 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.
|
||||
|
||||
int end = endRange[1];
|
||||
int quietEnd = end + (end - endRange[0]);
|
||||
if (quietEnd >= row->getSize() || !row->isRange(end, quietEnd, false)) {
|
||||
throw NotFoundException();
|
||||
}
|
||||
|
||||
Ref<String> resultString (new String(result));
|
||||
if (!checkChecksum(resultString)) {
|
||||
throw ChecksumException();
|
||||
}
|
||||
|
||||
float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f;
|
||||
float right = (float) (endRange[1] + endRange[0]) / 2.0f;
|
||||
BarcodeFormat format = getBarcodeFormat();
|
||||
ArrayRef< Ref<ResultPoint> > resultPoints(2);
|
||||
resultPoints[0] = Ref<ResultPoint>(new OneDResultPoint(left, (float) rowNumber));
|
||||
resultPoints[1] = Ref<ResultPoint>(new OneDResultPoint(right, (float) rowNumber));
|
||||
Ref<Result> decodeResult (new Result(resultString, ArrayRef<char>(), resultPoints, format));
|
||||
// Java extension and man stuff
|
||||
return decodeResult;
|
||||
}
|
||||
|
||||
UPCEANReader::Range UPCEANReader::findStartGuardPattern(Ref<BitArray> row) {
|
||||
bool foundStart = false;
|
||||
Range startRange;
|
||||
int nextStart = 0;
|
||||
vector<int> counters(START_END_PATTERN.size(), 0);
|
||||
// std::cerr << "fsgp " << *row << std::endl;
|
||||
while (!foundStart) {
|
||||
for(int i=0; i < (int)START_END_PATTERN.size(); ++i) {
|
||||
counters[i] = 0;
|
||||
}
|
||||
|
||||
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() {
|
||||
startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN, counters);
|
||||
// std::cerr << "sr " << startRange[0] << " " << startRange[1] << std::endl;
|
||||
int start = startRange[0];
|
||||
nextStart = startRange[1];
|
||||
// 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) {
|
||||
foundStart = row->isRange(quietStart, start, false);
|
||||
}
|
||||
}
|
||||
return startRange;
|
||||
}
|
||||
|
||||
UPCEANReader::Range UPCEANReader::findGuardPattern(Ref<BitArray> row,
|
||||
int rowOffset,
|
||||
bool whiteFirst,
|
||||
vector<int> const& pattern) {
|
||||
vector<int> counters (pattern.size(), 0);
|
||||
return findGuardPattern(row, rowOffset, whiteFirst, pattern, counters);
|
||||
}
|
||||
|
||||
UPCEANReader::Range UPCEANReader::findGuardPattern(Ref<BitArray> row,
|
||||
int rowOffset,
|
||||
bool whiteFirst,
|
||||
vector<int> const& pattern,
|
||||
vector<int>& counters) {
|
||||
// cerr << "fGP " << rowOffset << " " << whiteFirst << endl;
|
||||
if (false) {
|
||||
for(int i=0; i < (int)pattern.size(); ++i) {
|
||||
std::cerr << pattern[i];
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
int patternLength = pattern.size();
|
||||
int width = row->getSize();
|
||||
bool isWhite = whiteFirst;
|
||||
rowOffset = whiteFirst ? row->getNextUnset(rowOffset) : row->getNextSet(rowOffset);
|
||||
int counterPosition = 0;
|
||||
int patternStart = rowOffset;
|
||||
for (int x = rowOffset; x < width; x++) {
|
||||
// std::cerr << "rg " << x << " " << row->get(x) << std::endl;
|
||||
if (row->get(x) ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
|
||||
return Range(patternStart, x);
|
||||
}
|
||||
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 NotFoundException();
|
||||
}
|
||||
|
||||
UPCEANReader::Range UPCEANReader::decodeEnd(Ref<BitArray> row, int endStart) {
|
||||
return findGuardPattern(row, endStart, false, START_END_PATTERN);
|
||||
}
|
||||
|
||||
int UPCEANReader::decodeDigit(Ref<BitArray> row,
|
||||
vector<int> & counters,
|
||||
int rowOffset,
|
||||
vector<int const*> const& patterns) {
|
||||
recordPattern(row, rowOffset, counters);
|
||||
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
||||
int bestMatch = -1;
|
||||
int max = patterns.size();
|
||||
for (int i = 0; i < max; i++) {
|
||||
int const* pattern (patterns[i]);
|
||||
int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
|
||||
if (variance < bestVariance) {
|
||||
bestVariance = variance;
|
||||
bestMatch = i;
|
||||
}
|
||||
}
|
||||
if (bestMatch >= 0) {
|
||||
return bestMatch;
|
||||
} else {
|
||||
throw NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link #checkStandardUPCEANChecksum(String)}
|
||||
*/
|
||||
bool UPCEANReader::checkChecksum(Ref<String> const& 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(Ref<String> const& s_) {
|
||||
std::string const& s (s_->getText());
|
||||
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() {
|
||||
}
|
||||
|
|
|
@ -22,54 +22,67 @@
|
|||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
typedef enum UPC_EAN_PATTERNS {
|
||||
UPC_EAN_PATTERNS_L_PATTERNS = 0,
|
||||
UPC_EAN_PATTERNS_L_AND_G_PATTERNS
|
||||
} UPC_EAN_PATTERNS;
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class UPCEANReader : public OneDReader {
|
||||
|
||||
private:
|
||||
enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 420/1000)};
|
||||
enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 700/1000)};
|
||||
|
||||
static bool findStartGuardPattern(Ref<BitArray> row, int* rangeStart, int* rangeEnd);
|
||||
|
||||
virtual bool decodeEnd(Ref<BitArray> row, int endStart, int* endGuardBegin, int* endGuardEnd);
|
||||
|
||||
static bool checkStandardUPCEANChecksum(std::string s);
|
||||
protected:
|
||||
static bool findGuardPattern(Ref<BitArray> row, int rowOffset, bool whiteFirst,
|
||||
const int pattern[], int patternLen, int* start, int* end);
|
||||
|
||||
virtual int getMIDDLE_PATTERN_LEN();
|
||||
virtual const int* getMIDDLE_PATTERN();
|
||||
|
||||
public:
|
||||
UPCEANReader();
|
||||
|
||||
// Returns < 0 on failure, >= 0 on success.
|
||||
virtual int decodeMiddle(Ref<BitArray> row, int startGuardBegin, int startGuardEnd,
|
||||
std::string& resultString) = 0;
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
|
||||
// TODO(dswitkin): Should this be virtual so that UPCAReader can override it?
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row, int startGuardBegin,
|
||||
int startGuardEnd);
|
||||
|
||||
// Returns < 0 on failure, >= 0 on success.
|
||||
static int decodeDigit(Ref<BitArray> row, int counters[], int countersLen, int rowOffset,
|
||||
UPC_EAN_PATTERNS patternType);
|
||||
|
||||
virtual bool checkChecksum(std::string s);
|
||||
|
||||
virtual BarcodeFormat getBarcodeFormat() = 0;
|
||||
virtual ~UPCEANReader();
|
||||
};
|
||||
}
|
||||
class MultiFormatUPCEANReader;
|
||||
class UPCEANReader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::UPCEANReader : public OneDReader {
|
||||
friend class MultiFormatUPCEANReader;
|
||||
private:
|
||||
std::string decodeRowStringBuffer;
|
||||
// UPCEANExtensionSupport extensionReader;
|
||||
// EANManufacturerOrgSupport eanManSupport;
|
||||
|
||||
static const int MAX_AVG_VARIANCE;
|
||||
static const int MAX_INDIVIDUAL_VARIANCE;
|
||||
|
||||
static Range findStartGuardPattern(Ref<BitArray> row);
|
||||
|
||||
virtual Range decodeEnd(Ref<BitArray> row, int endStart);
|
||||
|
||||
static bool checkStandardUPCEANChecksum(Ref<String> const& s);
|
||||
|
||||
static Range findGuardPattern(Ref<BitArray> row,
|
||||
int rowOffset,
|
||||
bool whiteFirst,
|
||||
std::vector<int> const& pattern,
|
||||
std::vector<int>& counters);
|
||||
|
||||
|
||||
protected:
|
||||
static const std::vector<int> START_END_PATTERN;
|
||||
static const std::vector<int> MIDDLE_PATTERN;
|
||||
|
||||
static const std::vector<int const*> L_PATTERNS;
|
||||
static const std::vector<int const*> L_AND_G_PATTERNS;
|
||||
|
||||
static Range findGuardPattern(Ref<BitArray> row,
|
||||
int rowOffset,
|
||||
bool whiteFirst,
|
||||
std::vector<int> const& pattern);
|
||||
|
||||
public:
|
||||
UPCEANReader();
|
||||
|
||||
virtual int decodeMiddle(Ref<BitArray> row,
|
||||
Range const& startRange,
|
||||
std::string& resultString) = 0;
|
||||
|
||||
virtual Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
virtual Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row, Range const& range);
|
||||
|
||||
static int decodeDigit(Ref<BitArray> row,
|
||||
std::vector<int>& counters,
|
||||
int rowOffset,
|
||||
std::vector<int const*> const& patterns);
|
||||
|
||||
virtual bool checkChecksum(Ref<String> const& s);
|
||||
|
||||
virtual BarcodeFormat getBarcodeFormat() = 0;
|
||||
virtual ~UPCEANReader();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,125 +18,125 @@
|
|||
#include "UPCEReader.h"
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using zxing::Ref;
|
||||
using zxing::String;
|
||||
using zxing::oned::UPCEReader;
|
||||
|
||||
/**
|
||||
* 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};
|
||||
#define VECTOR_INIT(v) v, v + sizeof(v)/sizeof(v[0])
|
||||
|
||||
/**
|
||||
* 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}
|
||||
};
|
||||
namespace {
|
||||
/**
|
||||
* The pattern that marks the middle, and end, of a UPC-E pattern.
|
||||
* There is no "second half" to a UPC-E barcode.
|
||||
*/
|
||||
const int MIDDLE_END_PATTERN_[6] = {1, 1, 1, 1, 1, 1};
|
||||
const vector<int> MIDDLE_END_PATTERN (VECTOR_INIT(MIDDLE_END_PATTERN_));
|
||||
|
||||
|
||||
UPCEReader::UPCEReader() {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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, Range const& startRange, string& result) {
|
||||
vector<int>& counters (decodeMiddleCounters);
|
||||
counters.clear();
|
||||
counters.resize(4);
|
||||
int end = row->getSize();
|
||||
int rowOffset = startRange[1];
|
||||
|
||||
int lgPatternFound = 0;
|
||||
|
||||
for (int x = 0; x < 6 && rowOffset < end; x++) {
|
||||
int bestMatch = decodeDigit(row, counters, rowOffset, L_AND_G_PATTERNS);
|
||||
result.append(1, (char) ('0' + bestMatch % 10));
|
||||
for (int i = 0, e = counters.size(); i < e; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
|
||||
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;
|
||||
if (bestMatch >= 10) {
|
||||
lgPatternFound |= 1 << (5 - x);
|
||||
}
|
||||
}
|
||||
|
||||
determineNumSysAndCheckDigit(result, lgPatternFound);
|
||||
|
||||
return rowOffset;
|
||||
}
|
||||
|
||||
UPCEReader::Range UPCEReader::decodeEnd(Ref<BitArray> row, int endStart) {
|
||||
return findGuardPattern(row, endStart, true, MIDDLE_END_PATTERN);
|
||||
}
|
||||
|
||||
bool UPCEReader::checkChecksum(Ref<String> const& 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
|
||||
*/
|
||||
Ref<String> UPCEReader::convertUPCEtoUPCA(Ref<String> const& upce_) {
|
||||
string const& upce(upce_->getText());
|
||||
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 Ref<String>(new String(result));
|
||||
}
|
||||
|
||||
|
||||
zxing::BarcodeFormat UPCEReader::getBarcodeFormat() {
|
||||
return BarcodeFormat::UPC_E;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
|
||||
#ifndef __UPC_E_READER_H__
|
||||
#define __UPC_E_READER_H__
|
||||
|
||||
|
@ -22,23 +23,25 @@
|
|||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class UPCEReader : public UPCEANReader {
|
||||
|
||||
private:
|
||||
static bool determineNumSysAndCheckDigit(std::string& resultString, int lgPatternFound);
|
||||
protected:
|
||||
bool decodeEnd(Ref<BitArray> row, int endStart, int* endGuardBegin, int* endGuardEnd);
|
||||
bool checkChecksum(std::string s);
|
||||
public:
|
||||
UPCEReader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, int startGuardBegin, int startGuardEnd,
|
||||
std::string& resultString);
|
||||
static std::string convertUPCEtoUPCA(std::string upce);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
};
|
||||
class UPCEReader;
|
||||
}
|
||||
}
|
||||
|
||||
class zxing::oned::UPCEReader : public UPCEANReader {
|
||||
private:
|
||||
std::vector<int> decodeMiddleCounters;
|
||||
static bool determineNumSysAndCheckDigit(std::string& resultString, int lgPatternFound);
|
||||
|
||||
protected:
|
||||
Range decodeEnd(Ref<BitArray> row, int endStart);
|
||||
bool checkChecksum(Ref<String> const& s);
|
||||
public:
|
||||
UPCEReader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, Range const& startRange, std::string& resultString);
|
||||
static Ref<String> convertUPCEtoUPCA(Ref<String> const& upce);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -39,19 +39,18 @@ 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_(ErrorCorrectionLevel::forBits((formatInfo >> 3) & 0x03)), dataMask_((char)(formatInfo & 0x07)) {
|
||||
}
|
||||
|
||||
ErrorCorrectionLevel& FormatInformation::getErrorCorrectionLevel() {
|
||||
return errorCorrectionLevel_;
|
||||
}
|
||||
|
||||
unsigned char FormatInformation::getDataMask() {
|
||||
char FormatInformation::getDataMask() {
|
||||
return dataMask_;
|
||||
}
|
||||
|
||||
int FormatInformation::numBitsDiffering(unsigned int a, unsigned int b) {
|
||||
int FormatInformation::numBitsDiffering(int a, 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)]
|
||||
|
|
|
@ -35,16 +35,16 @@ private:
|
|||
static int BITS_SET_IN_HALF_BYTE[];
|
||||
|
||||
ErrorCorrectionLevel &errorCorrectionLevel_;
|
||||
unsigned char dataMask_;
|
||||
char dataMask_;
|
||||
|
||||
FormatInformation(int formatInfo);
|
||||
|
||||
public:
|
||||
static int numBitsDiffering(unsigned int a, unsigned int b);
|
||||
static int numBitsDiffering(int a, int b);
|
||||
static Ref<FormatInformation> decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2);
|
||||
static Ref<FormatInformation> doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2);
|
||||
ErrorCorrectionLevel &getErrorCorrectionLevel();
|
||||
unsigned char getDataMask();
|
||||
char getDataMask();
|
||||
friend bool operator==(const FormatInformation &a, const FormatInformation &b);
|
||||
friend std::ostream& operator<<(std::ostream& out, const FormatInformation& fi);
|
||||
};
|
||||
|
|
|
@ -49,8 +49,7 @@ namespace zxing {
|
|||
cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush;
|
||||
#endif
|
||||
|
||||
std::vector<Ref<ResultPoint> > points(detectorResult->getPoints());
|
||||
|
||||
ArrayRef< Ref<ResultPoint> > points (detectorResult->getPoints());
|
||||
|
||||
#ifdef DEBUG
|
||||
cout << "(3) extracted points " << &points << "\n" << flush;
|
||||
|
@ -68,7 +67,7 @@ namespace zxing {
|
|||
#endif
|
||||
|
||||
Ref<Result> result(
|
||||
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_QR_CODE));
|
||||
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat::QR_CODE));
|
||||
#ifdef DEBUG
|
||||
cout << "(5) created result " << result.object_ << ", returning\n" << flush;
|
||||
#endif
|
||||
|
|
|
@ -116,7 +116,7 @@ Version *BitMatrixParser::readVersion() {
|
|||
throw ReaderException("Could not decode version");
|
||||
}
|
||||
|
||||
ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
|
||||
ArrayRef<char> BitMatrixParser::readCodewords() {
|
||||
Ref<FormatInformation> formatInfo = readFormatInformation();
|
||||
Version *version = readVersion();
|
||||
|
||||
|
@ -141,7 +141,7 @@ ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
|
|||
// cout << *functionPattern << endl;
|
||||
|
||||
bool readingUp = true;
|
||||
ArrayRef<unsigned char> result(version->getTotalCodewords());
|
||||
ArrayRef<char> result(version->getTotalCodewords());
|
||||
int resultOffset = 0;
|
||||
int currentByte = 0;
|
||||
int bitsRead = 0;
|
||||
|
@ -166,7 +166,7 @@ ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
|
|||
}
|
||||
// If we've made a whole byte, save it off
|
||||
if (bitsRead == 8) {
|
||||
result[resultOffset++] = (unsigned char)currentByte;
|
||||
result[resultOffset++] = (char)currentByte;
|
||||
bitsRead = 0;
|
||||
currentByte = 0;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
BitMatrixParser(Ref<BitMatrix> bitMatrix);
|
||||
Ref<FormatInformation> readFormatInformation();
|
||||
Version *readVersion();
|
||||
ArrayRef<unsigned char> readCodewords();
|
||||
ArrayRef<char> readCodewords();
|
||||
|
||||
private:
|
||||
BitMatrixParser(const BitMatrixParser&);
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace qrcode {
|
|||
|
||||
using namespace std;
|
||||
|
||||
DataBlock::DataBlock(int numDataCodewords, ArrayRef<unsigned char> codewords) :
|
||||
DataBlock::DataBlock(int numDataCodewords, ArrayRef<char> codewords) :
|
||||
numDataCodewords_(numDataCodewords), codewords_(codewords) {
|
||||
}
|
||||
|
||||
|
@ -34,12 +34,12 @@ int DataBlock::getNumDataCodewords() {
|
|||
return numDataCodewords_;
|
||||
}
|
||||
|
||||
ArrayRef<unsigned char> DataBlock::getCodewords() {
|
||||
ArrayRef<char> DataBlock::getCodewords() {
|
||||
return codewords_;
|
||||
}
|
||||
|
||||
|
||||
std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<unsigned char> rawCodewords, Version *version,
|
||||
std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<char> rawCodewords, Version *version,
|
||||
ErrorCorrectionLevel &ecLevel) {
|
||||
|
||||
|
||||
|
@ -63,7 +63,7 @@ std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<unsigned char> ra
|
|||
for (int i = 0; i < ecBlock->getCount(); i++) {
|
||||
int numDataCodewords = ecBlock->getDataCodewords();
|
||||
int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;
|
||||
ArrayRef<unsigned char> buffer(numBlockCodewords);
|
||||
ArrayRef<char> buffer(numBlockCodewords);
|
||||
Ref<DataBlock> blockRef(new DataBlock(numDataCodewords, buffer));
|
||||
result[numResultBlocks++] = blockRef;
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<unsigned char> ra
|
|||
}
|
||||
}
|
||||
|
||||
if ((size_t)rawCodewordsOffset != rawCodewords.size()) {
|
||||
if (rawCodewordsOffset != rawCodewords.size()) {
|
||||
throw IllegalArgumentException("rawCodewordsOffset != rawCodewords.length");
|
||||
}
|
||||
|
||||
|
|
|
@ -32,16 +32,16 @@ namespace qrcode {
|
|||
class DataBlock : public Counted {
|
||||
private:
|
||||
int numDataCodewords_;
|
||||
ArrayRef<unsigned char> codewords_;
|
||||
ArrayRef<char> codewords_;
|
||||
|
||||
DataBlock(int numDataCodewords, ArrayRef<unsigned char> codewords);
|
||||
DataBlock(int numDataCodewords, ArrayRef<char> codewords);
|
||||
|
||||
public:
|
||||
static std::vector<Ref<DataBlock> >
|
||||
getDataBlocks(ArrayRef<unsigned char> rawCodewords, Version *version, ErrorCorrectionLevel &ecLevel);
|
||||
getDataBlocks(ArrayRef<char> rawCodewords, Version *version, ErrorCorrectionLevel &ecLevel);
|
||||
|
||||
int getNumDataCodewords();
|
||||
ArrayRef<unsigned char> getCodewords();
|
||||
ArrayRef<char> getCodewords();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -55,11 +55,11 @@ 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);
|
||||
append(result, (char const*)in.c_str(), in.length(), src);
|
||||
}
|
||||
|
||||
void DecodedBitStreamParser::append(std::string &result,
|
||||
const unsigned char *bufIn,
|
||||
const char *bufIn,
|
||||
size_t nIn,
|
||||
const char *src) {
|
||||
#ifndef NO_ICONV
|
||||
|
@ -74,7 +74,7 @@ void DecodedBitStreamParser::append(std::string &result,
|
|||
}
|
||||
|
||||
const int maxOut = 4 * nIn + 1;
|
||||
unsigned char* bufOut = new unsigned char[maxOut];
|
||||
char* bufOut = new char[maxOut];
|
||||
|
||||
ICONV_CONST char *fromPtr = (ICONV_CONST char *)bufIn;
|
||||
size_t nFrom = nIn;
|
||||
|
@ -112,7 +112,7 @@ void DecodedBitStreamParser::decodeHanziSegment(Ref<BitSource> bits_,
|
|||
// 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];
|
||||
char* buffer = new char[nBytes];
|
||||
int offset = 0;
|
||||
while (count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
|
@ -125,8 +125,8 @@ void DecodedBitStreamParser::decodeHanziSegment(Ref<BitSource> bits_,
|
|||
// In the 0xB0A1 to 0xFAFE range
|
||||
assembledTwoBytes += 0x0A6A1;
|
||||
}
|
||||
buffer[offset] = (unsigned char) ((assembledTwoBytes >> 8) & 0xFF);
|
||||
buffer[offset + 1] = (unsigned char) (assembledTwoBytes & 0xFF);
|
||||
buffer[offset] = (char) ((assembledTwoBytes >> 8) & 0xFF);
|
||||
buffer[offset + 1] = (char) (assembledTwoBytes & 0xFF);
|
||||
offset += 2;
|
||||
count--;
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ void DecodedBitStreamParser::decodeKanjiSegment(Ref<BitSource> bits, std::string
|
|||
// 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];
|
||||
char* buffer = new char[nBytes];
|
||||
int offset = 0;
|
||||
while (count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
|
@ -159,8 +159,8 @@ void DecodedBitStreamParser::decodeKanjiSegment(Ref<BitSource> bits, std::string
|
|||
// In the 0xE040 to 0xEBBF range
|
||||
assembledTwoBytes += 0x0C140;
|
||||
}
|
||||
buffer[offset] = (unsigned char)(assembledTwoBytes >> 8);
|
||||
buffer[offset + 1] = (unsigned char)assembledTwoBytes;
|
||||
buffer[offset] = (char)(assembledTwoBytes >> 8);
|
||||
buffer[offset + 1] = (char)assembledTwoBytes;
|
||||
offset += 2;
|
||||
count--;
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ void DecodedBitStreamParser::decodeByteSegment(Ref<BitSource> bits_,
|
|||
string& result,
|
||||
int count,
|
||||
CharacterSetECI* currentCharacterSetECI,
|
||||
ArrayRef< ArrayRef<unsigned char> >& byteSegments,
|
||||
ArrayRef< ArrayRef<char> >& byteSegments,
|
||||
Hashtable const& hints) {
|
||||
int nBytes = count;
|
||||
BitSource& bits (*bits_);
|
||||
|
@ -182,10 +182,10 @@ void DecodedBitStreamParser::decodeByteSegment(Ref<BitSource> bits_,
|
|||
throw FormatException();
|
||||
}
|
||||
|
||||
ArrayRef<unsigned char> bytes_ (count);
|
||||
unsigned char* readBytes = &(*bytes_)[0];
|
||||
ArrayRef<char> bytes_ (count);
|
||||
char* readBytes = &(*bytes_)[0];
|
||||
for (int i = 0; i < count; i++) {
|
||||
readBytes[i] = (unsigned char) bits.readBits(8);
|
||||
readBytes[i] = (char) bits.readBits(8);
|
||||
}
|
||||
string encoding;
|
||||
if (currentCharacterSetECI == 0) {
|
||||
|
@ -208,7 +208,7 @@ void DecodedBitStreamParser::decodeByteSegment(Ref<BitSource> bits_,
|
|||
|
||||
void DecodedBitStreamParser::decodeNumericSegment(Ref<BitSource> bits, std::string &result, int count) {
|
||||
int nBytes = count;
|
||||
unsigned char* bytes = new unsigned char[nBytes];
|
||||
char* bytes = new char[nBytes];
|
||||
int i = 0;
|
||||
// Read three digits at a time
|
||||
while (count >= 3) {
|
||||
|
@ -335,7 +335,7 @@ namespace {
|
|||
}
|
||||
|
||||
Ref<DecoderResult>
|
||||
DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes,
|
||||
DecodedBitStreamParser::decode(ArrayRef<char> bytes,
|
||||
Version* version,
|
||||
ErrorCorrectionLevel const& ecLevel,
|
||||
Hashtable const& hints) {
|
||||
|
@ -344,7 +344,7 @@ DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes,
|
|||
string result;
|
||||
CharacterSetECI* currentCharacterSetECI = 0;
|
||||
bool fc1InEffect = false;
|
||||
ArrayRef< ArrayRef<unsigned char> > byteSegments (size_t(0));
|
||||
ArrayRef< ArrayRef<char> > byteSegments (0);
|
||||
Mode* mode = 0;
|
||||
do {
|
||||
// While still another segment to read...
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue