Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/zxing-cpp/core/src/ReadBarcode.cpp @ 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/ReadBarcode.cpp Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,219 @@ +/* +* Copyright 2019 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "ReadBarcode.h" + +#if !defined(ZXING_READERS) && !defined(ZXING_WRITERS) +#include "Version.h" +#endif + +#ifdef ZXING_READERS +#include "GlobalHistogramBinarizer.h" +#include "HybridBinarizer.h" +#include "MultiFormatReader.h" +#include "Pattern.h" +#include "ThresholdBinarizer.h" +#endif + +#include <climits> +#include <memory> +#include <stdexcept> + +namespace ZXing { + +#ifdef ZXING_READERS + +class LumImage : public Image +{ +public: + using Image::Image; + + uint8_t* data() { return const_cast<uint8_t*>(Image::data()); } +}; + +template<typename P> +static LumImage ExtractLum(const ImageView& iv, P projection) +{ + LumImage res(iv.width(), iv.height()); + + auto* dst = res.data(); + for(int y = 0; y < iv.height(); ++y) + for(int x = 0, w = iv.width(); x < w; ++x) + *dst++ = projection(iv.data(x, y)); + + return res; +} + +class LumImagePyramid +{ + std::vector<LumImage> buffers; + + template<int N> + void addLayer() + { + auto siv = layers.back(); + buffers.emplace_back(siv.width() / N, siv.height() / N); + layers.push_back(buffers.back()); + auto& div = buffers.back(); + auto* d = div.data(); + + for (int dy = 0; dy < div.height(); ++dy) + for (int dx = 0; dx < div.width(); ++dx) { + int sum = (N * N) / 2; + for (int ty = 0; ty < N; ++ty) + for (int tx = 0; tx < N; ++tx) + sum += *siv.data(dx * N + tx, dy * N + ty); + *d++ = sum / (N * N); + } + } + + void addLayer(int factor) + { + // help the compiler's auto-vectorizer by hard-coding the scale factor + switch (factor) { + case 2: addLayer<2>(); break; + case 3: addLayer<3>(); break; + case 4: addLayer<4>(); break; + default: throw std::invalid_argument("Invalid ReaderOptions::downscaleFactor"); break; + } + } + +public: + std::vector<ImageView> layers; + + LumImagePyramid(const ImageView& iv, int threshold, int factor) + { + if (factor < 2) + throw std::invalid_argument("Invalid ReaderOptions::downscaleFactor"); + + layers.push_back(iv); + // TODO: if only matrix codes were considered, then using std::min would be sufficient (see #425) + while (threshold > 0 && std::max(layers.back().width(), layers.back().height()) > threshold && + std::min(layers.back().width(), layers.back().height()) >= factor) + addLayer(factor); +#if 0 + // Reversing the layers means we'd start with the smallest. that can make sense if we are only looking for a + // single symbol. If we start with the higher resolution, we get better (high res) position information. + // TODO: see if masking out higher res layers based on found symbols in lower res helps overall performance. + std::reverse(layers.begin(), layers.end()); +#endif + } +}; + +ImageView SetupLumImageView(ImageView iv, LumImage& lum, const ReaderOptions& opts) +{ + if (iv.format() == ImageFormat::None) + throw std::invalid_argument("Invalid image format"); + + if (opts.binarizer() == Binarizer::GlobalHistogram || opts.binarizer() == Binarizer::LocalAverage) { + // manually spell out the 3 most common pixel formats to get at least gcc to vectorize the code + if (iv.format() == ImageFormat::RGB && iv.pixStride() == 3) { + lum = ExtractLum(iv, [](const uint8_t* src) { return RGBToLum(src[0], src[1], src[2]); }); + } else if (iv.format() == ImageFormat::RGBA && iv.pixStride() == 4) { + lum = ExtractLum(iv, [](const uint8_t* src) { return RGBToLum(src[0], src[1], src[2]); }); + } else if (iv.format() == ImageFormat::BGR && iv.pixStride() == 3) { + lum = ExtractLum(iv, [](const uint8_t* src) { return RGBToLum(src[2], src[1], src[0]); }); + } else if (iv.format() != ImageFormat::Lum) { + lum = ExtractLum(iv, [r = RedIndex(iv.format()), g = GreenIndex(iv.format()), b = BlueIndex(iv.format())]( + const uint8_t* src) { return RGBToLum(src[r], src[g], src[b]); }); + } else if (iv.pixStride() != 1) { + // GlobalHistogram and LocalAverage need dense line memory layout + lum = ExtractLum(iv, [](const uint8_t* src) { return *src; }); + } + if (lum.data()) + return lum; + } + return iv; +} + +std::unique_ptr<BinaryBitmap> CreateBitmap(ZXing::Binarizer binarizer, const ImageView& iv) +{ + switch (binarizer) { + case Binarizer::BoolCast: return std::make_unique<ThresholdBinarizer>(iv, 0); + case Binarizer::FixedThreshold: return std::make_unique<ThresholdBinarizer>(iv, 127); + case Binarizer::GlobalHistogram: return std::make_unique<GlobalHistogramBinarizer>(iv); + case Binarizer::LocalAverage: return std::make_unique<HybridBinarizer>(iv); + } + return {}; // silence gcc warning +} + +Barcode ReadBarcode(const ImageView& _iv, const ReaderOptions& opts) +{ + return FirstOrDefault(ReadBarcodes(_iv, ReaderOptions(opts).setMaxNumberOfSymbols(1))); +} + +Barcodes ReadBarcodes(const ImageView& _iv, const ReaderOptions& opts) +{ + if (sizeof(PatternType) < 4 && (_iv.width() > 0xffff || _iv.height() > 0xffff)) + throw std::invalid_argument("Maximum image width/height is 65535"); + + if (!_iv.data() || _iv.width() * _iv.height() == 0) + throw std::invalid_argument("ImageView is null/empty"); + + LumImage lum; + ImageView iv = SetupLumImageView(_iv, lum, opts); + MultiFormatReader reader(opts); + + if (opts.isPure()) + return {reader.read(*CreateBitmap(opts.binarizer(), iv)).setReaderOptions(opts)}; + + std::unique_ptr<MultiFormatReader> closedReader; +#ifdef ZXING_EXPERIMENTAL_API + auto formatsBenefittingFromClosing = BarcodeFormat::Aztec | BarcodeFormat::DataMatrix | BarcodeFormat::QRCode | BarcodeFormat::MicroQRCode; + ReaderOptions closedOptions = opts; + if (opts.tryDenoise() && opts.hasFormat(formatsBenefittingFromClosing) && _iv.height() >= 3) { + closedOptions.setFormats((opts.formats().empty() ? BarcodeFormat::Any : opts.formats()) & formatsBenefittingFromClosing); + closedReader = std::make_unique<MultiFormatReader>(closedOptions); + } +#endif + LumImagePyramid pyramid(iv, opts.downscaleThreshold() * opts.tryDownscale(), opts.downscaleFactor()); + + Barcodes res; + int maxSymbols = opts.maxNumberOfSymbols() ? opts.maxNumberOfSymbols() : INT_MAX; + for (auto&& iv : pyramid.layers) { + auto bitmap = CreateBitmap(opts.binarizer(), iv); + for (int close = 0; close <= (closedReader ? 1 : 0); ++close) { + if (close) + bitmap->close(); + + // TODO: check if closing after invert would be beneficial + for (int invert = 0; invert <= static_cast<int>(opts.tryInvert() && !close); ++invert) { + if (invert) + bitmap->invert(); + auto rs = (close ? *closedReader : reader).readMultiple(*bitmap, maxSymbols); + for (auto& r : rs) { + if (iv.width() != _iv.width()) + r.setPosition(Scale(r.position(), _iv.width() / iv.width())); + if (!Contains(res, r)) { + r.setReaderOptions(opts); + r.setIsInverted(bitmap->inverted()); + res.push_back(std::move(r)); + --maxSymbols; + } + } + if (maxSymbols <= 0) + return res; + } + } + } + + return res; +} + +#else // ZXING_READERS + +Barcode ReadBarcode(const ImageView&, const ReaderOptions&) +{ + throw std::runtime_error("This build of zxing-cpp does not support reading barcodes."); +} + +Barcodes ReadBarcodes(const ImageView&, const ReaderOptions&) +{ + throw std::runtime_error("This build of zxing-cpp does not support reading barcodes."); +} + +#endif // ZXING_READERS + +} // ZXing
