diff --git a/3rdparty/ZxingQtReader.hpp b/3rdparty/ZxingQtReader.hpp index 423c756..4aa2d15 100644 --- a/3rdparty/ZxingQtReader.hpp +++ b/3rdparty/ZxingQtReader.hpp @@ -1,28 +1,24 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser - * - * 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. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ZXing/ReadBarcode.h" #include #include #include +#include #ifdef QT_MULTIMEDIA_LIB +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include +#else +#include +#include +#endif #include #endif @@ -30,167 +26,176 @@ namespace ZXingQt { - Q_NAMESPACE +Q_NAMESPACE //TODO: find a better way to export these enums to QML than to duplicate their definition // #ifdef Q_MOC_RUN produces meta information in the moc output but it does end up working in qml #ifdef QT_QML_LIB - enum class BarcodeFormat +enum class BarcodeFormat { None = 0, ///< Used as a return value if no valid barcode has been detected - Aztec = (1 << 0), ///< Aztec (2D) - Codabar = (1 << 1), ///< Codabar (1D) - Code39 = (1 << 2), ///< Code39 (1D) - Code93 = (1 << 3), ///< Code93 (1D) - Code128 = (1 << 4), ///< Code128 (1D) + Aztec = (1 << 0), ///< Aztec + Codabar = (1 << 1), ///< Codabar + Code39 = (1 << 2), ///< Code39 + Code93 = (1 << 3), ///< Code93 + Code128 = (1 << 4), ///< Code128 DataBar = (1 << 5), ///< GS1 DataBar, formerly known as RSS 14 DataBarExpanded = (1 << 6), ///< GS1 DataBar Expanded, formerly known as RSS EXPANDED - DataMatrix = (1 << 7), ///< DataMatrix (2D) - EAN8 = (1 << 8), ///< EAN-8 (1D) - EAN13 = (1 << 9), ///< EAN-13 (1D) - ITF = (1 << 10), ///< ITF (Interleaved Two of Five) (1D) - MaxiCode = (1 << 11), ///< MaxiCode (2D) - PDF417 = (1 << 12), ///< PDF417 (1D) or (2D) - QRCode = (1 << 13), ///< QR Code (2D) - UPCA = (1 << 14), ///< UPC-A (1D) - UPCE = (1 << 15), ///< UPC-E (1D) - MicroQRCode = (1 << 16), ///< Micro QR Code (2D) + DataMatrix = (1 << 7), ///< DataMatrix + EAN8 = (1 << 8), ///< EAN-8 + EAN13 = (1 << 9), ///< EAN-13 + ITF = (1 << 10), ///< ITF (Interleaved Two of Five) + MaxiCode = (1 << 11), ///< MaxiCode + PDF417 = (1 << 12), ///< PDF417 or + QRCode = (1 << 13), ///< QR Code + UPCA = (1 << 14), ///< UPC-A + UPCE = (1 << 15), ///< UPC-E + MicroQRCode = (1 << 16), ///< Micro QR Code - OneDCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, - TwoDCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, + LinearCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, + MatrixCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, }; -enum class DecodeStatus +enum class ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI }; + +#else +using ZXing::BarcodeFormat; +using ZXing::ContentType; +#endif + +using ZXing::DecodeHints; +using ZXing::Binarizer; +using ZXing::BarcodeFormats; + +Q_ENUM_NS(BarcodeFormat) +Q_ENUM_NS(ContentType) + +template +QDebug operator<<(QDebug dbg, const T& v) { - NoError = 0, - NotFound, - FormatError, - ChecksumError, + return dbg.noquote() << QString::fromStdString(ToString(v)); +} + +class Position : public ZXing::Quadrilateral +{ + Q_GADGET + + Q_PROPERTY(QPoint topLeft READ topLeft) + Q_PROPERTY(QPoint topRight READ topRight) + Q_PROPERTY(QPoint bottomRight READ bottomRight) + Q_PROPERTY(QPoint bottomLeft READ bottomLeft) + + using Base = ZXing::Quadrilateral; + +public: + using Base::Base; }; -#else - using ZXing::BarcodeFormat; - using ZXing::DecodeStatus; -#endif - using ZXing::DecodeHints; - using ZXing::Binarizer; - using ZXing::BarcodeFormats; +class Result : private ZXing::Result +{ + Q_GADGET - Q_ENUM_NS(BarcodeFormat) - Q_ENUM_NS(DecodeStatus) + Q_PROPERTY(BarcodeFormat format READ format) + Q_PROPERTY(QString formatName READ formatName) + Q_PROPERTY(QString text READ text) + Q_PROPERTY(QByteArray bytes READ bytes) + Q_PROPERTY(bool isValid READ isValid) + Q_PROPERTY(ContentType contentType READ contentType) + Q_PROPERTY(Position position READ position) - template - QDebug operator<<(QDebug dbg, const T& v) - { - return dbg.noquote() << QString::fromStdString(ToString(v)); - } + QString _text; + QByteArray _bytes; + Position _position; - class Position : public ZXing::Quadrilateral - { - Q_GADGET +public: + Result() = default; // required for qmetatype machinery - Q_PROPERTY(QPoint topLeft READ topLeft) - Q_PROPERTY(QPoint topRight READ topRight) - Q_PROPERTY(QPoint bottomRight READ bottomRight) - Q_PROPERTY(QPoint bottomLeft READ bottomLeft) + explicit Result(ZXing::Result&& r) : ZXing::Result(std::move(r)) { + _text = QString::fromStdString(ZXing::Result::text()); + _bytes = QByteArray(reinterpret_cast(ZXing::Result::bytes().data()), Size(ZXing::Result::bytes())); + auto& pos = ZXing::Result::position(); + auto qp = [&pos](int i) { return QPoint(pos[i].x, pos[i].y); }; + _position = {qp(0), qp(1), qp(2), qp(3)}; + } - using Base = ZXing::Quadrilateral; + using ZXing::Result::isValid; - public: - using Base::Base; - }; + BarcodeFormat format() const { return static_cast(ZXing::Result::format()); } + ContentType contentType() const { return static_cast(ZXing::Result::contentType()); } + QString formatName() const { return QString::fromStdString(ZXing::ToString(ZXing::Result::format())); } + const QString& text() const { return _text; } + const QByteArray& bytes() const { return _bytes; } + const Position& position() const { return _position; } - class Result : private ZXing::Result - { - Q_GADGET + // For debugging/development + int runTime = 0; + Q_PROPERTY(int runTime MEMBER runTime) +}; - Q_PROPERTY(BarcodeFormat format READ format) - Q_PROPERTY(QString formatName READ formatName) - Q_PROPERTY(QString text READ text) - Q_PROPERTY(QByteArray rawBytes READ rawBytes) - Q_PROPERTY(bool isValid READ isValid) - Q_PROPERTY(DecodeStatus status READ status) - Q_PROPERTY(Position position READ position) +inline QList QListResults(ZXing::Results&& zxres) +{ + QList res; + for (auto&& r : zxres) + res.push_back(Result(std::move(r))); + return res; +} - QString _text; - QByteArray _rawBytes; - Position _position; - - public: - Result() : ZXing::Result(ZXing::DecodeStatus::NotFound) {} // required for qmetatype machinery - - explicit Result(ZXing::Result&& r) : ZXing::Result(std::move(r)) { - _text = QString::fromWCharArray(ZXing::Result::text().c_str()); - _rawBytes = QByteArray(reinterpret_cast(ZXing::Result::rawBytes().data()), - Size(ZXing::Result::rawBytes())); - auto& pos = ZXing::Result::position(); - auto qp = [&pos](int i) { return QPoint(pos[i].x, pos[i].y); }; - _position = {qp(0), qp(1), qp(2), qp(3)}; - } - - using ZXing::Result::isValid; - - BarcodeFormat format() const { return static_cast(ZXing::Result::format()); } - DecodeStatus status() const { return static_cast(ZXing::Result::status()); } - QString formatName() const { return QString::fromStdString(ZXing::ToString(ZXing::Result::format())); } - const QString& text() const { return _text; } - const QByteArray& rawBytes() const { return _rawBytes; } - const Position& position() const { return _position; } - - // For debugging/development - int runTime = 0; - Q_PROPERTY(int runTime MEMBER runTime) - }; - - inline Result ReadBarcode(const QImage& img, const DecodeHints& hints = {}) - { - using namespace ZXing; - - auto ImgFmtFromQImg = [](const QImage& img) { - switch (img.format()) { - case QImage::Format_ARGB32: - case QImage::Format_RGB32: -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - return ImageFormat::BGRX; -#else - return ImageFormat::XRGB; -#endif - case QImage::Format_RGB888: return ImageFormat::RGB; - case QImage::Format_RGBX8888: - case QImage::Format_RGBA8888: return ImageFormat::RGBX; - case QImage::Format_Grayscale8: return ImageFormat::Lum; - default: return ImageFormat::None; - } - }; - - auto exec = [&](const QImage& img) { - return Result(ZXing::ReadBarcode( - {img.bits(), img.width(), img.height(), ImgFmtFromQImg(img), static_cast(img.bytesPerLine())}, hints)); - }; - - return ImgFmtFromQImg(img) == ImageFormat::None ? exec(img.convertToFormat(QImage::Format_Grayscale8)) : exec(img); - } - -#ifdef QT_MULTIMEDIA_LIB - inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = {}) +inline QList ReadBarcodes(const QImage& img, const DecodeHints& hints = {}) { using namespace ZXing; - auto img = frame; // shallow copy just get access to non-const map() function - if (!frame.isValid() || !img.map(QAbstractVideoBuffer::ReadOnly)){ - qWarning() << "invalid QVideoFrame: could not map memory"; - return {}; - } - //TODO c++17: SCOPE_EXIT([&] { img.unmap(); }); + auto ImgFmtFromQImg = [](const QImage& img) { + switch (img.format()) { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + return ImageFormat::BGRX; +#else + return ImageFormat::XRGB; +#endif + case QImage::Format_RGB888: return ImageFormat::RGB; + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: return ImageFormat::RGBX; + case QImage::Format_Grayscale8: return ImageFormat::Lum; + default: return ImageFormat::None; + } + }; + + auto exec = [&](const QImage& img) { + return QListResults(ZXing::ReadBarcodes( + {img.bits(), img.width(), img.height(), ImgFmtFromQImg(img), static_cast(img.bytesPerLine())}, hints)); + }; + + return ImgFmtFromQImg(img) == ImageFormat::None ? exec(img.convertToFormat(QImage::Format_Grayscale8)) : exec(img); +} + +inline Result ReadBarcode(const QImage& img, const DecodeHints& hints = {}) +{ + auto res = ReadBarcodes(img, DecodeHints(hints).setMaxNumberOfSymbols(1)); + return !res.isEmpty() ? res.takeFirst() : Result(); +} + +#ifdef QT_MULTIMEDIA_LIB +inline QList ReadBarcodes(const QVideoFrame& frame, const DecodeHints& hints = {}) +{ + using namespace ZXing; ImageFormat fmt = ImageFormat::None; int pixStride = 0; int pixOffset = 0; - switch (img.pixelFormat()) { - case QVideoFrame::Format_ARGB32: - case QVideoFrame::Format_ARGB32_Premultiplied: - case QVideoFrame::Format_RGB32: +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#define FORMAT(F5, F6) QVideoFrame::Format_##F5 +#define FIRST_PLANE +#else +#define FORMAT(F5, F6) QVideoFrameFormat::Format_##F6 +#define FIRST_PLANE 0 +#endif + + switch (frame.pixelFormat()) { + case FORMAT(ARGB32, ARGB8888): + case FORMAT(ARGB32_Premultiplied, ARGB8888_Premultiplied): + case FORMAT(RGB32, RGBX8888): #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN fmt = ImageFormat::BGRX; #else @@ -198,11 +203,9 @@ enum class DecodeStatus #endif break; - case QVideoFrame::Format_RGB24: fmt = ImageFormat::RGB; break; - - case QVideoFrame::Format_BGRA32: - case QVideoFrame::Format_BGRA32_Premultiplied: - case QVideoFrame::Format_BGR32: + case FORMAT(BGRA32, BGRA8888): + case FORMAT(BGRA32_Premultiplied, BGRA8888_Premultiplied): + case FORMAT(BGR32, BGRX8888): #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN fmt = ImageFormat::RGBX; #else @@ -210,10 +213,17 @@ enum class DecodeStatus #endif break; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + case QVideoFrame::Format_RGB24: fmt = ImageFormat::RGB; break; case QVideoFrame::Format_BGR24: fmt = ImageFormat::BGR; break; + case QVideoFrame::Format_YUV444: fmt = ImageFormat::Lum, pixStride = 3; break; +#else + case QVideoFrameFormat::Format_P010: + case QVideoFrameFormat::Format_P016: fmt = ImageFormat::Lum, pixStride = 1; break; +#endif - case QVideoFrame::Format_AYUV444: - case QVideoFrame::Format_AYUV444_Premultiplied: + case FORMAT(AYUV444, AYUV): + case FORMAT(AYUV444_Premultiplied, AYUV_Premultiplied): #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN fmt = ImageFormat::Lum, pixStride = 4, pixOffset = 3; #else @@ -221,23 +231,22 @@ enum class DecodeStatus #endif break; - case QVideoFrame::Format_YUV444: fmt = ImageFormat::Lum, pixStride = 3; break; - case QVideoFrame::Format_YUV420P: - case QVideoFrame::Format_NV12: - case QVideoFrame::Format_NV21: - case QVideoFrame::Format_IMC1: - case QVideoFrame::Format_IMC2: - case QVideoFrame::Format_IMC3: - case QVideoFrame::Format_IMC4: - case QVideoFrame::Format_YV12: fmt = ImageFormat::Lum; break; - case QVideoFrame::Format_UYVY: fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; - case QVideoFrame::Format_YUYV: fmt = ImageFormat::Lum, pixStride = 2; break; + case FORMAT(YUV420P, YUV420P): + case FORMAT(NV12, NV12): + case FORMAT(NV21, NV21): + case FORMAT(IMC1, IMC1): + case FORMAT(IMC2, IMC2): + case FORMAT(IMC3, IMC3): + case FORMAT(IMC4, IMC4): + case FORMAT(YV12, YV12): fmt = ImageFormat::Lum; break; + case FORMAT(UYVY, UYVY): fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; + case FORMAT(YUYV, YUYV): fmt = ImageFormat::Lum, pixStride = 2; break; - case QVideoFrame::Format_Y8: fmt = ImageFormat::Lum; break; - case QVideoFrame::Format_Y16: fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; + case FORMAT(Y8, Y8): fmt = ImageFormat::Lum; break; + case FORMAT(Y16, Y16): fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; #if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) - case QVideoFrame::Format_ABGR32: + case FORMAT(ABGR32, ABGR8888): #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN fmt = ImageFormat::RGBX; #else @@ -246,24 +255,47 @@ enum class DecodeStatus break; #endif #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - case QVideoFrame::Format_YUV422P: fmt = ImageFormat::Lum; break; + case FORMAT(YUV422P, YUV422P): fmt = ImageFormat::Lum; break; #endif default: break; } - Result res; if (fmt != ImageFormat::None) { - res = Result( - ZXing::ReadBarcode({img.bits() + pixOffset, img.width(), img.height(), fmt, img.bytesPerLine(), pixStride}, - hints)); - } else { - if (QVideoFrame::imageFormatFromPixelFormat(img.pixelFormat()) != QImage::Format_Invalid) - res = ReadBarcode(img.image(), hints); + auto img = frame; // shallow copy just get access to non-const map() function +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (!img.isValid() || !img.map(QAbstractVideoBuffer::ReadOnly)){ +#else + if (!img.isValid() || !img.map(QVideoFrame::ReadOnly)){ +#endif + qWarning() << "invalid QVideoFrame: could not map memory"; + return {}; + } + QScopeGuard unmap([&] { img.unmap(); }); + + return QListResults(ZXing::ReadBarcodes( + {img.bits(FIRST_PLANE) + pixOffset, img.width(), img.height(), fmt, img.bytesPerLine(FIRST_PLANE), pixStride}, hints)); } + else { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat()) != QImage::Format_Invalid) { + qWarning() << "unsupported QVideoFrame::pixelFormat"; + return {}; + } + auto qimg = frame.image(); +#else + auto qimg = frame.toImage(); +#endif + if (qimg.format() != QImage::Format_Invalid) + return ReadBarcodes(qimg, hints); + qWarning() << "failed to convert QVideoFrame to QImage"; + return {}; + } +} - img.unmap(); - - return res; +inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = {}) +{ + auto res = ReadBarcodes(frame, DecodeHints(hints).setMaxNumberOfSymbols(1)); + return !res.isEmpty() ? res.takeFirst() : Result(); } #define ZQ_PROPERTY(Type, name, setter) \ @@ -279,14 +311,21 @@ public: \ } \ Q_SIGNAL void name##Changed(); -class VideoFilter : public QAbstractVideoFilter, private DecodeHints + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +class BarcodeReader : public QAbstractVideoFilter, private DecodeHints +#else +class BarcodeReader : public QObject, private DecodeHints +#endif { Q_OBJECT public: - VideoFilter(QObject* parent = nullptr) : QAbstractVideoFilter(parent) {} - - QVideoFilterRunnable* createFilterRunnable() override; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + BarcodeReader(QObject* parent = nullptr) : QAbstractVideoFilter(parent) {} +#else + BarcodeReader(QObject* parent = nullptr) : QObject(parent) {} +#endif // TODO: find out how to properly expose QFlags to QML // simply using ZQ_PROPERTY(BarcodeFormats, formats, setFormats) @@ -309,9 +348,10 @@ public: ZQ_PROPERTY(bool, tryRotate, setTryRotate) ZQ_PROPERTY(bool, tryHarder, setTryHarder) + ZQ_PROPERTY(bool, tryDownscale, setTryDownscale) public slots: - Result process(const QVideoFrame& image) + ZXingQt::Result process(const QVideoFrame& image) { QElapsedTimer t; t.start(); @@ -327,18 +367,41 @@ public slots: } signals: - void newResult(Result result); - void foundBarcode(Result result); + void newResult(ZXingQt::Result result); + void foundBarcode(ZXingQt::Result result); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +public: + QVideoFilterRunnable *createFilterRunnable() override; +#else +private: + QVideoSink *_sink = nullptr; + +public: + void setVideoSink(QVideoSink* sink) { + if (_sink == sink) + return; + + if (_sink) + disconnect(_sink, nullptr, this, nullptr); + + _sink = sink; + connect(_sink, &QVideoSink::videoFrameChanged, this, &BarcodeReader::process); + } + Q_PROPERTY(QVideoSink* videoSink WRITE setVideoSink) +#endif + }; #undef ZX_PROPERTY +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) class VideoFilterRunnable : public QVideoFilterRunnable { - VideoFilter* _filter = nullptr; + BarcodeReader* _filter = nullptr; public: - explicit VideoFilterRunnable(VideoFilter* filter) : _filter(filter) {} + explicit VideoFilterRunnable(BarcodeReader* filter) : _filter(filter) {} QVideoFrame run(QVideoFrame* input, const QVideoSurfaceFormat& /*surfaceFormat*/, RunFlags /*flags*/) override { @@ -347,10 +410,11 @@ public: } }; -inline QVideoFilterRunnable* VideoFilter::createFilterRunnable() +inline QVideoFilterRunnable* BarcodeReader::createFilterRunnable() { return new VideoFilterRunnable(this); } +#endif #endif // QT_MULTIMEDIA_LIB @@ -369,7 +433,7 @@ namespace ZXingQt { inline void registerQmlAndMetaTypes() { qRegisterMetaType("BarcodeFormat"); - qRegisterMetaType("DecodeStatus"); + qRegisterMetaType("ContentType"); // supposedly the Q_DECLARE_METATYPE should be used with the overload without a custom name // but then the qml side complains about "unregistered type" @@ -378,7 +442,7 @@ inline void registerQmlAndMetaTypes() qmlRegisterUncreatableMetaObject( ZXingQt::staticMetaObject, "ZXing", 1, 0, "ZXing", "Access to enums & flags only"); - qmlRegisterType("ZXing", 1, 0, "VideoFilter"); + qmlRegisterType("ZXing", 1, 0, "BarcodeReader"); } } // namespace ZXingQt diff --git a/libs/build_deps_all.sh b/libs/build_deps_all.sh index 61e2bdf..a17b803 100755 --- a/libs/build_deps_all.sh +++ b/libs/build_deps_all.sh @@ -27,8 +27,8 @@ clean() { rm -rf dl.zip yaml-* zxing-* protobuf } -#### ZXing 1.3.0 #### -curl -L -o dl.zip https://github.com/nu-book/zxing-cpp/archive/refs/tags/v1.3.0.zip +#### ZXing v2.0.0 #### +curl -L -o dl.zip https://github.com/nu-book/zxing-cpp/archive/refs/tags/v2.0.0.zip unzip dl.zip cd zxing-* @@ -40,12 +40,6 @@ ninja && ninja install cd ../.. -#### exit if NKR_PACKAGE #### -if [ ! -z $NKR_PACKAGE ]; then - clean - exit -fi - #### yaml-cpp #### curl -L -o dl.zip https://github.com/jbeder/yaml-cpp/archive/refs/tags/yaml-cpp-0.7.0.zip unzip dl.zip