Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/zxing-cpp/core/src/Pattern.h @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/zxing-cpp/core/src/Pattern.h Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,412 @@ +/* +* Copyright 2020 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "BitHacks.h" +#include "Range.h" +#include "ZXAlgorithms.h" + +#include <algorithm> +#include <array> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <vector> + +namespace ZXing { + +using PatternType = uint16_t; +template<int N> using Pattern = std::array<PatternType, N>; +using PatternRow = std::vector<PatternType>; + +class PatternView +{ + using Iterator = PatternRow::const_pointer; + Iterator _data = nullptr; + int _size = 0; + Iterator _base = nullptr; + Iterator _end = nullptr; + +public: + using value_type = PatternRow::value_type; + + PatternView() = default; + + // A PatternRow always starts with the width of whitespace in front of the first black bar. + // The first element of the PatternView is the first bar. + PatternView(const PatternRow& bars) + : _data(bars.data() + 1), _size(Size(bars) - 1), _base(bars.data()), _end(bars.data() + bars.size()) + {} + + PatternView(Iterator data, int size, Iterator base, Iterator end) : _data(data), _size(size), _base(base), _end(end) {} + + template <size_t N> + PatternView(const Pattern<N>& row) : _data(row.data()), _size(N) + {} + + Iterator data() const { return _data; } + Iterator begin() const { return _data; } + Iterator end() const { return _data + _size; } + + value_type operator[](int i) const + { +// assert(i < _count); + return _data[i]; + } + + int sum(int n = 0) const { return Reduce(_data, _data + (n == 0 ? _size : n)); } + int size() const { return _size; } + + // index is the number of bars and spaces from the first bar to the current position + int index() const { return narrow_cast<int>(_data - _base) - 1; } + int pixelsInFront() const { return Reduce(_base, _data); } + int pixelsTillEnd() const { return Reduce(_base, _data + _size) - 1; } + bool isAtFirstBar() const { return _data == _base + 1; } + bool isAtLastBar() const { return _data + _size == _end - 1; } + bool isValid(int n) const { return _data && _data >= _base && _data + n <= _end; } + bool isValid() const { return isValid(size()); } + + template<bool acceptIfAtFirstBar = false> + bool hasQuietZoneBefore(float scale) const + { + return (acceptIfAtFirstBar && isAtFirstBar()) || _data[-1] >= sum() * scale; + } + + template<bool acceptIfAtLastBar = true> + bool hasQuietZoneAfter(float scale) const + { + return (acceptIfAtLastBar && isAtLastBar()) || _data[_size] >= sum() * scale; + } + + PatternView subView(int offset, int size = 0) const + { +// if(std::abs(size) > count()) +// printf("%d > %d\n", std::abs(size), _count); +// assert(std::abs(size) <= count()); + if (size == 0) + size = _size - offset; + else if (size < 0) + size = _size - offset + size; + return {begin() + offset, std::max(size, 0), _base, _end}; + } + + bool shift(int n) + { + return _data && ((_data += n) + _size <= _end); + } + + bool skipPair() + { + return shift(2); + } + + bool skipSymbol() + { + return shift(_size); + } + + bool skipSingle(int maxWidth) + { + return shift(1) && _data[-1] <= maxWidth; + } + + void extend() + { + _size = std::max(0, narrow_cast<int>(_end - _data)); + } +}; + +/** + * @brief The BarAndSpace struct is a simple 2 element data structure to hold information about bar(s) and space(s). + * + * The operator[](int) can be used in combination with a PatternView + */ +template <typename T> +struct BarAndSpace +{ + using value_type = T; + T bar = {}, space = {}; + // even index -> bar, odd index -> space + constexpr T& operator[](int i) noexcept { return reinterpret_cast<T*>(this)[i & 1]; } + constexpr T operator[](int i) const noexcept { return reinterpret_cast<const T*>(this)[i & 1]; } + bool isValid() const { return bar != T{} && space != T{}; } +}; + +using BarAndSpaceI = BarAndSpace<PatternType>; + +template <int LEN, typename RT, typename T> +constexpr auto BarAndSpaceSum(const T* view) noexcept +{ + BarAndSpace<RT> res; + for (int i = 0; i < LEN; ++i) + res[i] += view[i]; + return res; +} + +/** + * @brief FixedPattern describes a compile-time constant (start/stop) pattern. + * + * N = number of bars/spaces + * SUM = sum over all N elements (size of pattern in modules) + * IS_SPARCE = whether or not the pattern contains '0's denoting 'wide' bars/spaces + */ +template <int N, int SUM, bool IS_SPARCE = false> +struct FixedPattern +{ + using value_type = PatternRow::value_type; + value_type _data[N]; + constexpr value_type operator[](int i) const noexcept { return _data[i]; } + constexpr const value_type* data() const noexcept { return _data; } + constexpr int size() const noexcept { return N; } + constexpr BarAndSpace<value_type> sums() const noexcept { return BarAndSpaceSum<N, value_type>(_data); } +}; + +template <int N, int SUM> +using FixedSparcePattern = FixedPattern<N, SUM, true>; + +template <bool E2E = false, int LEN, int SUM> +double IsPattern(const PatternView& view, const FixedPattern<LEN, SUM, false>& pattern, int spaceInPixel = 0, + double minQuietZone = 0, double moduleSizeRef = 0) +{ + if constexpr (E2E) { + auto widths = BarAndSpaceSum<LEN, double>(view.data()); + auto sums = pattern.sums(); + BarAndSpace<double> modSize = {widths[0] / sums[0], widths[1] / sums[1]}; + + auto [m, M] = std::minmax(modSize[0], modSize[1]); + if (M > 4 * m) // make sure module sizes of bars and spaces are not too far away from each other + return 0; + + if (minQuietZone && spaceInPixel < minQuietZone * modSize.space) + return 0; + + const BarAndSpace<double> thr = {modSize[0] * .75 + .5, modSize[1] / (2 + (LEN < 6)) + .5}; + + for (int x = 0; x < LEN; ++x) + if (std::abs(view[x] - pattern[x] * modSize[x]) > thr[x]) + return 0; + + return (modSize[0] + modSize[1]) / 2; + } + + double width = view.sum(LEN); + if (SUM > LEN && width < SUM) + return 0; + + const auto moduleSize = width / SUM; + + if (minQuietZone && spaceInPixel < minQuietZone * moduleSize - 1) + return 0; + + if (!moduleSizeRef) + moduleSizeRef = moduleSize; + + // the offset of 0.5 is to make the code less sensitive to quantization errors for small (near 1) module sizes. + // TODO: review once we have upsampling in the binarizer in place. + const auto threshold = moduleSizeRef * (0.5 + E2E * 0.25) + 0.5; + + for (int x = 0; x < LEN; ++x) + if (std::abs(view[x] - pattern[x] * moduleSizeRef) > threshold) + return 0; + + return moduleSize; +} + +template <bool RELAXED_THRESHOLD = false, int N, int SUM> +double IsPattern(const PatternView& view, const FixedPattern<N, SUM, true>& pattern, int spaceInPixel = 0, + double minQuietZone = 0, double moduleSizeRef = 0) +{ + // pattern contains the indices with the bars/spaces that need to be equally wide + double width = 0; + for (int x = 0; x < SUM; ++x) + width += view[pattern[x]]; + + const auto moduleSize = width / SUM; + + if (minQuietZone && spaceInPixel < minQuietZone * moduleSize - 1) + return 0; + + if (!moduleSizeRef) + moduleSizeRef = moduleSize; + + // the offset of 0.5 is to make the code less sensitive to quantization errors for small (near 1) module sizes. + // TODO: review once we have upsampling in the binarizer in place. + const auto threshold = moduleSizeRef * (0.5 + RELAXED_THRESHOLD * 0.25) + 0.5; + + for (int x = 0; x < SUM; ++x) + if (std::abs(view[pattern[x]] - moduleSizeRef) > threshold) + return 0; + + return moduleSize; +} + +template <int N, int SUM, bool IS_SPARCE> +bool IsRightGuard(const PatternView& view, const FixedPattern<N, SUM, IS_SPARCE>& pattern, double minQuietZone, + double moduleSizeRef = 0) +{ + int spaceInPixel = view.isAtLastBar() ? std::numeric_limits<int>::max() : *view.end(); + return IsPattern(view, pattern, spaceInPixel, minQuietZone, moduleSizeRef) != 0; +} + +template<int LEN, typename Pred> +PatternView FindLeftGuard(const PatternView& view, int minSize, Pred isGuard) +{ + if (view.size() < minSize) + return {}; + + auto window = view.subView(0, LEN); + if (window.isAtFirstBar() && isGuard(window, std::numeric_limits<int>::max())) + return window; + for (auto end = view.end() - minSize; window.data() < end; window.skipPair()) + if (isGuard(window, window[-1])) + return window; + + return {}; +} + +template <int LEN, int SUM, bool IS_SPARCE> +PatternView FindLeftGuard(const PatternView& view, int minSize, const FixedPattern<LEN, SUM, IS_SPARCE>& pattern, + double minQuietZone) +{ + return FindLeftGuard<LEN>(view, std::max(minSize, LEN), + [&pattern, minQuietZone](const PatternView& window, int spaceInPixel) { + return IsPattern(window, pattern, spaceInPixel, minQuietZone); + }); +} + +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)) / mods; + std::array<int, LEN - 2> e2e; + + for (int i = 0; i < LEN - 2; i++) { + int i_v = reverse ? LEN - 2 - i : i; + double v = (view[i_v] + view[i_v + 1]) / moduleSize; + e2e[i] = int(v + .5); + } + + return e2e; +} + +template <int LEN, int SUM> +std::array<int, LEN> NormalizedPattern(const PatternView& view) +{ + double moduleSize = static_cast<double>(view.sum(LEN)) / SUM; +#if 1 + int err = SUM; + std::array<int, LEN> is; + std::array<double, LEN> rs; + for (int i = 0; i < LEN; i++) { + double v = view[i] / moduleSize; + is[i] = int(v + .5); + rs[i] = v - is[i]; + err -= is[i]; + } + + if (std::abs(err) > 1) + return {}; + + if (err) { + auto mi = err > 0 ? std::max_element(std::begin(rs), std::end(rs)) - std::begin(rs) + : std::min_element(std::begin(rs), std::end(rs)) - std::begin(rs); + is[mi] += err; + rs[mi] -= err; + } +#else + std::array<int, LEN> is, e2e; + int min_v = view[0], min_i = 0; + + for (int i = 1; i < LEN; i++) { + double v = (view[i - 1] + view[i]) / moduleSize; + e2e[i] = int(v + .5); + if (view[i] < min_v) { + min_v = view[i]; + min_i = i; + } + } + + is[min_i] = 1; + for (int i = min_i + 1; i < LEN; ++i) + is[i] = e2e[i] - is[i - 1]; + for (int i = min_i - 1; i >= 0; --i) + is[i] = e2e[i + 1] - is[i + 1]; +#endif + return is; +} + +template<typename I> +void GetPatternRow(Range<I> b_row, PatternRow& p_row) +{ + // TODO: if reactivating the bit-packed array (!ZX_FAST_BIT_STORAGE) should be of interest then the following code could be + // considerably speed up by using a specialized variant along the lines of the old BitArray::getNextSetTo() function that + // was removed between 1.4 and 2.0. + +#if 0 + p_row.reserve(64); + p_row.clear(); + + auto lastPos = b_row.begin(); + if (*lastPos) + p_row.push_back(0); // first value is number of white pixels, here 0 + + for (auto p = b_row.begin() + 1; p < b_row.end(); ++p) + if (bool(*p) != bool(*lastPos)) + p_row.push_back(p - std::exchange(lastPos, p)); + + p_row.push_back(b_row.end() - lastPos); + + if (*lastPos) + p_row.push_back(0); // last value is number of white pixels, here 0 +#else + p_row.resize(b_row.size() + 2); + std::fill(p_row.begin(), p_row.end(), 0); + + auto bitPos = b_row.begin(); + const auto bitPosEnd = b_row.end(); + auto intPos = p_row.data(); + + if (*bitPos) + intPos++; // first value is number of white pixels, here 0 + + // The following code as been observed to cause a speedup of up to 30% on large images on an AVX cpu + // and on an a Google Pixel 3 Android phone. Your mileage may vary. + if constexpr (std::is_pointer_v<I> && sizeof(I) == 8 && sizeof(std::remove_pointer_t<I>) == 1) { + using simd_t = uint64_t; + while (bitPos < bitPosEnd - sizeof(simd_t)) { + auto asSimd0 = BitHacks::LoadU<simd_t>(bitPos); + auto asSimd1 = BitHacks::LoadU<simd_t>(bitPos + 1); + auto z = asSimd0 ^ asSimd1; + if (z) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + int step = BitHacks::NumberOfTrailingZeros(z) / 8 + 1; +#else + int step = BitHacks::NumberOfLeadingZeros(z) / 8 + 1; +#endif + (*intPos++) += step; + bitPos += step; + } else { + (*intPos) += sizeof(simd_t); + bitPos += sizeof(simd_t); + } + } + } + + while (++bitPos != bitPosEnd) { + ++(*intPos); + intPos += bitPos[0] != bitPos[-1]; + } + ++(*intPos); + + if (bitPos[-1]) + intPos++; + + p_row.resize(intPos - p_row.data() + 1); +#endif +} + +} // ZXing
