Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/nu-book/zxing-cpp
Browse files Browse the repository at this point in the history
  • Loading branch information
markusfisch committed Nov 7, 2024
2 parents a91b26a + 579650a commit 90403e0
Show file tree
Hide file tree
Showing 37 changed files with 175 additions and 123 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.16)

project(ZXing)

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Thanks a lot for your contribution!
[Note:]
* DataBar used to be called RSS.
* DataBar, DX Film Edge, MaxiCode, Micro QR Code and rMQR Code are not supported for writing.
* Building with only C++17 (see [CMakeLists.txt](https://github.com/zxing-cpp/zxing-cpp/blob/d4b0f502775857f257d13efd25fb840ece1bca3e/CMakeLists.txt#L45)) changes the behaviour of the library: it then lacks support for DataBarLimited and multi-symbol and position independent detection for DataMatrix.
* Building with only C++17 (see [CMakeLists.txt](https://github.com/zxing-cpp/zxing-cpp/blob/d4b0f502775857f257d13efd25fb840ece1bca3e/CMakeLists.txt#L45)) changes the behavior of the library: it then lacks support for DataBarLimited and multi-symbol and position independent detection for DataMatrix.

## Getting Started

Expand Down Expand Up @@ -79,7 +79,7 @@ int main(int argc, char** argv)
```
To see the full capability of the API, have a look at [`ZXingReader.cpp`](example/ZXingReader.cpp).
[Note: At least C++17 is reqired on the client side to use the API.]
[Note: At least C++17 is required on the client side to use the API.]
### To write barcodes:
1. Create a [`MultiFormatWriter`](core/src/MultiFormatWriter.h) instance with the format you want to generate. Set encoding and margins if needed.
Expand All @@ -98,7 +98,7 @@ As an example, have a look at [`ZXingWriter.cpp`](example/ZXingWriter.cpp).
## Build Instructions
These are the generic instructions to build the library on Windows/macOS/Linux. For details on how to build the individual wrappers, follow the links above.
1. Make sure [CMake](https://cmake.org) version 3.15 or newer is installed.
1. Make sure [CMake](https://cmake.org) version 3.16 or newer is installed. The python module requires 3.18 or higher.
2. Make sure a sufficiently C++20 compliant compiler is installed (minimum VS 2019 16.10? / gcc 11 / clang 12?).
3. See the cmake `ZXING_...` options to enable the testing code, python wrapper, etc.
Expand Down
2 changes: 1 addition & 1 deletion core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.16)

project (ZXing VERSION "2.2.1")
set (ZXING_SONAME 3) # see https://github.com/zxing-cpp/zxing-cpp/issues/333
Expand Down
7 changes: 6 additions & 1 deletion core/src/BitHacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@
#include <cstring>
#include <vector>

#if __has_include(<bit>) && __cplusplus > 201703L // MSVC has the <bit> header but then warns about including it
// MSVC has the <bit> header but then warns about including it.
// We check for _MSVC_LANG here as well, so client code is depending on /Zc:__cplusplus
#if __has_include(<bit>) && (__cplusplus > 201703L || _MSVC_LANG > 201703L)
#include <bit>
#if __cplusplus > 201703L && defined(__ANDROID__) // NDK 25.1.8937393 has the implementation but fails to advertise it
#define __cpp_lib_bitops 201907L
#endif
#elif defined(_MSC_VER)
// accoring to #863 MSVC defines __cpp_lib_bitops even when <bit> it not included and bitops are not available
#undef __cpp_lib_bitops
#endif

#if defined(__clang__) || defined(__GNUC__)
Expand Down
9 changes: 5 additions & 4 deletions core/src/Pattern.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,14 +278,15 @@ PatternView FindLeftGuard(const PatternView& view, int minSize, const FixedPatte
});
}

template <int LEN, int SUM>
std::array<int, LEN - 2> NormalizedE2EPattern(const PatternView& view)
template <int LEN>
std::array<int, LEN - 2> NormalizedE2EPattern(const PatternView& view, int mods, bool reverse = false)
{
double moduleSize = static_cast<double>(view.sum(LEN)) / SUM;
double moduleSize = static_cast<double>(view.sum(LEN)) / mods;
std::array<int, LEN - 2> e2e;

for (int i = 0; i < LEN - 2; i++) {
double v = (view[i] + view[i + 1]) / moduleSize;
int i_v = reverse ? LEN - 2 - i : i;
double v = (view[i_v] + view[i_v + 1]) / moduleSize;
e2e[i] = int(v + .5);
}

Expand Down
4 changes: 2 additions & 2 deletions core/src/oned/ODCode128Reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ class Raw2TxtDecoder
constexpr auto START_PATTERN_PREFIX = FixedPattern<3, 4>{2, 1, 1};
constexpr int CHAR_LEN = 6;
constexpr float QUIET_ZONE = 5; // quiet zone spec is 10 modules, real world examples ignore that, see #138
constexpr int CHAR_SUM = 11;
constexpr int CHAR_MODS = 11;

//TODO: make this a constexpr variable initialization
static auto E2E_PATTERNS = [] {
Expand All @@ -180,7 +180,7 @@ Barcode Code128Reader::decodePattern(int rowNumber, PatternView& next, std::uniq
int minCharCount = 4; // start + payload + checksum + stop
auto decodePattern = [](const PatternView& view, bool start = false) {
// This is basically the reference algorithm from the specification
int code = IndexOf(E2E_PATTERNS, ToInt(NormalizedE2EPattern<CHAR_LEN, CHAR_SUM>(view)));
int code = IndexOf(E2E_PATTERNS, ToInt(NormalizedE2EPattern<CHAR_LEN>(view, CHAR_MODS)));
if (code == -1 && !start) // if the reference algo fails, give the original upstream version a try (required to decode a few samples)
code = DecodeDigit(view, Code128::CODE_PATTERNS, MAX_AVG_VARIANCE, MAX_INDIVIDUAL_VARIANCE);
return code;
Expand Down
9 changes: 9 additions & 0 deletions core/src/oned/ODDataBarCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ using Array4F = std::array<float, 4>;
bool ReadDataCharacterRaw(const PatternView& view, int numModules, bool reversed, Array4I& oddPattern,
Array4I& evnPattern)
{
#if 1
auto pattern = NormalizedPatternFromE2E<8>(view, numModules, reversed);
OddEven<Array4I&> res = {oddPattern, evnPattern};

for (int i = 0; i < Size(pattern); ++i)
res[i % 2][i / 2] = pattern[i];

#else
OddEven<Array4I&> res = {oddPattern, evnPattern};
OddEven<Array4F> rem;
Expand All @@ -86,6 +94,7 @@ bool ReadDataCharacterRaw(const PatternView& view, int numModules, bool reversed
res[i % 2][i / 2] = int(v + .5f);
rem[i % 2][i / 2] = v - res[i % 2][i / 2];
}
#endif

// DataBarExpanded data character is 17 modules wide
// DataBar outer data character is 16 modules wide
Expand Down
65 changes: 58 additions & 7 deletions core/src/oned/ODDataBarCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,22 @@ struct PairHash
constexpr int FULL_PAIR_SIZE = 8 + 5 + 8;
constexpr int HALF_PAIR_SIZE = 8 + 5 + 2; // half has to be followed by a guard pattern

template<typename T>
int ParseFinderPattern(const PatternView& view, bool reversed, T l2rPattern, T r2lPattern)
template<int N>
int ParseFinderPattern(const PatternView& view, bool reversed, const std::array<std::array<int, 3>, N>& e2ePatterns)
{
constexpr float MAX_AVG_VARIANCE = 0.2f;
constexpr float MAX_INDIVIDUAL_VARIANCE = 0.45f;

int i = 1 + RowReader::DecodeDigit(view, reversed ? r2lPattern : l2rPattern, MAX_AVG_VARIANCE,
MAX_INDIVIDUAL_VARIANCE, true);
const auto e2e = NormalizedE2EPattern<5>(view, 15, reversed);

int best_i, best_e = 3;
for (int i = 0; i < Size(e2ePatterns); ++i) {
int e = 0;
for (int j = 0; j < 3; ++j)
e += std::abs(e2ePatterns[i][j] - e2e[j]);
if (e < best_e) {
best_e = e;
best_i = i;
}
}
int i = best_e <= 1 ? 1 + best_i : 0;
return reversed ? -i : i;
}

Expand All @@ -134,6 +142,49 @@ struct OddEven

using Array4I = std::array<int, 4>;

// elements() determines the element widths of an (n,k) character with
// at least one even-numbered element that's just one module wide.
// (Note: even-numbered elements - 2nd, 4th, 6th, etc., have odd indexes)
// for DataBarLimited: LEN=14, mods=26/18
template <int LEN>
std::array<int, LEN> NormalizedPatternFromE2E(const PatternView& view, int mods, bool reversed = false)
{
bool isExp = mods == 17; // elementsExp() with at least one odd-numbered element that's just one module wide
const auto e2e = NormalizedE2EPattern<LEN>(view, mods, reversed);
std::array<int, LEN> widths;

// derive element widths from normalized edge-to-similar-edge measurements
int barSum = widths[0] = isExp ? 8 : 1; // first assume 1st bar is 1 / 8
for (int i = 0; i < Size(e2e); i++) {
widths[i + 1] = e2e[i] - widths[i];
barSum += widths[i + 1];
}
widths.back() = mods - barSum; // last even element makes mods modules

// int minEven = widths[1];
// for (int i = 3; i < Size(widths); i += 2)
// minEven = std::min(minEven, widths[i]);
OddEven<int> min = {widths[0], widths[1]};
for (int i = 2; i < Size(widths); i++)
min[i] = std::min(min[i], widths[i]);

if (isExp && min[0] > 1) {
// minimum odd width is too big, readjust so minimum odd is 1
for (int i = 0; i < Size(widths); i += 2) {
widths[i] -= min[0] - 1;
widths[i + 1] += min[0] - 1;
}
} else if (!isExp && min[1] > 1) {
// minimum even width is too big, readjust so minimum even is 1
for (int i = 0; i < Size(widths); i += 2) {
widths[i] += min[1] - 1;
widths[i + 1] -= min[1] - 1;
}
}

return widths;
}

bool ReadDataCharacterRaw(const PatternView& view, int numModules, bool reversed, Array4I& oddPattern,
Array4I& evnPattern);

Expand Down
19 changes: 8 additions & 11 deletions core/src/oned/ODDataBarExpandedReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,16 @@ static const std::array<int, 7> VALID_HALF_PAIRS = {{-FINDER_A, FINDER_B, -FINDE

static int ParseFinderPattern(const PatternView& view, Direction dir)
{
static constexpr std::array<FixedPattern<5, 15>, 6> FINDER_PATTERNS = {{
{1, 8, 4, 1, 1}, // A
{3, 6, 4, 1, 1}, // B
{3, 4, 6, 1, 1}, // C
{3, 2, 8, 1, 1}, // D
{2, 6, 5, 1, 1}, // E
{2, 2, 9, 1, 1}, // F
static constexpr std::array<std::array<int, 3>, 6> e2ePatterns = {{
{9, 12, 5 }, // {1, 8, 4, 1, 1}, // A
{9, 10, 5 }, // {3, 6, 4, 1, 1}, // B
{7, 10, 7 }, // {3, 4, 6, 1, 1}, // C
{5, 10, 9 }, // {3, 2, 8, 1, 1}, // D
{8, 11, 6 }, // {2, 6, 5, 1, 1}, // E
{4, 11, 10}, // {2, 2, 9, 1, 1}, // F
}};
// TODO: c++20 constexpr inversion from FIND_PATTERN?
static constexpr std::array<FixedPattern<5, 15>, 6> REVERSED_FINDER_PATTERNS = {
{{1, 1, 4, 8, 1}, {1, 1, 4, 6, 3}, {1, 1, 6, 4, 3}, {1, 1, 8, 2, 3}, {1, 1, 5, 6, 2}, {1, 1, 9, 2, 2}}};

return ParseFinderPattern(view, dir == Direction::Left, FINDER_PATTERNS, REVERSED_FINDER_PATTERNS);
return ParseFinderPattern<6>(view, dir == Direction::Left, e2ePatterns);
}

static bool ChecksumIsValid(const Pairs& pairs)
Expand Down
39 changes: 3 additions & 36 deletions core/src/oned/ODDataBarLimitedReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,47 +27,14 @@ using namespace DataBar;
constexpr int CHAR_LEN = 14;
constexpr int SYMBOL_LEN = 1 + 3 * CHAR_LEN + 2;

// elements() determines the element widths of an (n,k) character with
// at least one even-numbered element that's just one module wide.
// (Note: even-numbered elements - 2nd, 4th, 6th, etc., have odd indexes)
// for DataBarLimited: SUM=26/18, LEN=14
template <int LEN, int SUM>
std::array<int, LEN> NormalizedPatternFromE2E(const PatternView& view)
{
auto e2e = NormalizedE2EPattern<LEN, SUM>(view);
std::array<int, LEN> widths;

// derive element widths from normalized edge-to-similar-edge measurements
int barSum = widths[0] = 1; // first assume 1st bar is 1
for (int i = 0; i < Size(e2e); i++) {
widths[i + 1] = e2e[i] - widths[i];
barSum += widths[i + 1];
}
widths.back() = SUM - barSum; // last even element makes SUM modules

int minEven = widths[1];
for (int i = 3; i < Size(widths); i += 2)
minEven = std::min(minEven, widths[i]);

if (minEven > 1) {
// minimum even width is too big, readjust so minimum even is 1
for (int i = 0; i < Size(widths); i += 2) {
widths[i] += minEven - 1;
widths[i + 1] -= minEven - 1;
}
}

return widths;
}

static Character ReadDataCharacter(const PatternView& view)
{
constexpr int G_SUM[] = {0, 183064, 820064, 1000776, 1491021, 1979845, 1996939};
constexpr int T_EVEN[] = {28, 728, 6454, 203, 2408, 1, 16632};
constexpr int ODD_SUM[] = {17, 13, 9, 15, 11, 19, 7};
constexpr int ODD_WIDEST[] = {6, 5, 3, 5, 4, 8, 1};

auto pattern = NormalizedPatternFromE2E<14, 26>(view);
auto pattern = NormalizedPatternFromE2E<14>(view, 26);

int checkSum = 0;
for (auto it = pattern.rbegin(); it != pattern.rend(); ++it)
Expand Down Expand Up @@ -170,13 +137,13 @@ Barcode DataBarLimitedReader::decodePattern(int rowNumber, PatternView& next, st
if ((!next.isAtFirstBar() && next[-1] < modSize) || (!next.isAtLastBar() && next[SYMBOL_LEN] < 5 * modSize))
continue;

auto checkCharPattern = ToInt(NormalizedPatternFromE2E<CHAR_LEN, 18>(checkView));
auto checkCharPattern = ToInt(NormalizedPatternFromE2E<CHAR_LEN>(checkView, 18));
int checkSum = IndexOf(CheckChars, checkCharPattern);
if (checkSum == -1)
continue;

printf("%f - ", modSize);
printv("%d ", NormalizedPatternFromE2E<CHAR_LEN, 18>(checkView));
printv("%d ", NormalizedPatternFromE2E<CHAR_LEN>(checkView, 18));

auto left = ReadDataCharacter(leftView);
auto right = ReadDataCharacter(rightView);
Expand Down
28 changes: 11 additions & 17 deletions core/src/oned/ODDataBarReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,19 @@ static Character ReadDataCharacter(const PatternView& view, bool outsideChar, bo

int ParseFinderPattern(const PatternView& view, bool reversed)
{
static constexpr std::array<FixedPattern<5, 15>, 10> FINDER_PATTERNS = {{
{3, 8, 2, 1, 1},
{3, 5, 5, 1, 1},
{3, 3, 7, 1, 1},
{3, 1, 9, 1, 1},
{2, 7, 4, 1, 1},
{2, 5, 6, 1, 1},
{2, 3, 8, 1, 1},
{1, 5, 7, 1, 1},
{1, 3, 9, 1, 1},
static constexpr std::array<std::array<int, 3>, 9> e2ePatterns = {{
{11, 10, 3 }, // {3, 8, 2, 1, 1}
{8 , 10, 6 }, // {3, 5, 5, 1, 1}
{6 , 10, 8 }, // {3, 3, 7, 1, 1}
{4 , 10, 10}, // {3, 1, 9, 1, 1}
{9 , 11, 5 }, // {2, 7, 4, 1, 1}
{7 , 11, 7 }, // {2, 5, 6, 1, 1}
{5 , 11, 9 }, // {2, 3, 8, 1, 1}
{6 , 11, 8 }, // {1, 5, 7, 1, 1}
{4 , 12, 10}, // {1, 3, 9, 1, 1}
}};

// TODO: c++20 constexpr inversion from FIND_PATTERN?
static constexpr std::array<FixedPattern<5, 15>, 10> REVERSED_FINDER_PATTERNS = {{
{1, 1, 2, 8, 3}, {1, 1, 5, 5, 3}, {1, 1, 7, 3, 3}, {1, 1, 9, 1, 3}, {1, 1, 4, 7, 2},
{1, 1, 6, 5, 2}, {1, 1, 8, 3, 2}, {1, 1, 7, 5, 1}, {1, 1, 9, 3, 1},
}};

return ParseFinderPattern(view, reversed, FINDER_PATTERNS, REVERSED_FINDER_PATTERNS);
return ParseFinderPattern<9>(view, reversed, e2ePatterns);
}

static Pair ReadPair(const PatternView& view, bool rightPair)
Expand Down
27 changes: 17 additions & 10 deletions core/src/oned/ODITFReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace ZXing::OneD {

Barcode ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr<DecodingState>&) const
{
const int minCharCount = 6;
const int minCharCount = _opts.formats().count() == 1 ? 4 : 6; // if we are only looking for ITF, we accept shorter symbols
const int minQuietZone = 6; // spec requires 10

next = FindLeftGuard(next, 4 + minCharCount/2 + 3, FixedPattern<4, 4>{1, 1, 1, 1}, minQuietZone);
Expand All @@ -33,46 +33,53 @@ Barcode ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_p

constexpr int weights[] = {1, 2, 4, 7, 0};
int xStart = next.pixelsInFront();
bool startsAtFirstBar = next.isAtFirstBar();

next = next.subView(4, 10);

std::string txt;
txt.reserve(20);

while (next.isValid()) {
threshold = NarrowWideThreshold(next);
if (!threshold.isValid())
// look for end-of-symbol
if (next[3] > threshold.space * 3)
break;

BarAndSpace<int> digits, numWide;
bool bad = false;
for (int i = 0; i < 10; ++i) {
if (next[i] > threshold[i] * 2)
break;
bad |= next[i] > threshold[i] * 3 || next[i] < threshold[i] / 3;
numWide[i] += next[i] > threshold[i];
digits[i] += weights[i/2] * (next[i] > threshold[i]);
}

if (numWide.bar != 2 || numWide.space != 2)
if (bad || numWide.bar != 2 || numWide.space != 2)
break;

for (int i = 0; i < 2; ++i)
txt.push_back(ToDigit(digits[i] == 11 ? 0 : digits[i]));

// update threshold to support scanning slanted symbols (scanned non-perpendicular)
threshold = NarrowWideThreshold(next);

next.skipSymbol();
}

next = next.subView(0, 3);

if (Size(txt) < minCharCount || !next.isValid())
return {};

// Check quiet zone size
if (!(next.isAtLastBar() || next[3] > minQuietZone * (threshold.bar + threshold.space) / 3))
if (!next.isValid() || !threshold.isValid()
|| !(next.isAtLastBar() || next[3] > minQuietZone * (threshold.bar + threshold.space) / 3))
return {};

// Check stop pattern
if (next[0] < threshold[0] || next[1] > threshold[1] || next[2] > threshold[2])
return {};

// Check min length depending on whether the code covers the complete image or not
if (Size(txt) < (startsAtFirstBar && next.isAtLastBar() ? (minCharCount / 2) : minCharCount))
return {};

Error error = _opts.validateITFCheckSum() && !GTIN::IsCheckDigitValid(txt) ? ChecksumError() : Error();

// Symbology identifier ISO/IEC 16390:2007 Annex C Table C.1
Expand Down
Loading

0 comments on commit 90403e0

Please sign in to comment.