Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package prison-qt5 for openSUSE:Factory checked in at 2022-05-16 18:07:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/prison-qt5 (Old) and /work/SRC/openSUSE:Factory/.prison-qt5.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "prison-qt5" Mon May 16 18:07:25 2022 rev:67 rq:977213 version:5.94.0 Changes: -------- --- /work/SRC/openSUSE:Factory/prison-qt5/prison-qt5.changes 2022-04-11 23:49:27.654921677 +0200 +++ /work/SRC/openSUSE:Factory/.prison-qt5.new.1538/prison-qt5.changes 2022-05-16 18:09:36.185329983 +0200 @@ -1,0 +2,16 @@ +Tue May 10 08:18:15 UTC 2022 - Christophe Giboudeaux <christo...@krop.fr> + +- Update to 5.94.0 + * New feature release + * For more details please see: + * https://kde.org/announcements/frameworks/5/5.94.0 +- Changes since 5.93.0: + * Remove duplicate header between .h/.cpp file + * Fix out-of-bounds read on the Aztec special char table + * Enable macOS support + * Fix PrisonScanner target name + * Consider flipped video frames when computing the barcode positioon + * Add barcode scanner component for barcode scanning from live video + * Add windows CI + +------------------------------------------------------------------- Old: ---- prison-5.93.0.tar.xz prison-5.93.0.tar.xz.sig New: ---- prison-5.94.0.tar.xz prison-5.94.0.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ prison-qt5.spec ++++++ --- /var/tmp/diff_new_pack.8rskVo/_old 2022-05-16 18:09:36.657330358 +0200 +++ /var/tmp/diff_new_pack.8rskVo/_new 2022-05-16 18:09:36.665330364 +0200 @@ -19,7 +19,7 @@ %define sonum 5 %define rname prison %define _libname KF5Prison -%define _tar_path 5.93 +%define _tar_path 5.94 # Full KF5 version (e.g. 5.33.0) %{!?_kf5_version: %global _kf5_version %{version}} # Last major and minor KF5 version (e.g. 5.33) @@ -27,7 +27,7 @@ # Only needed for the package signature condition %bcond_without released Name: prison-qt5 -Version: 5.93.0 +Version: 5.94.0 Release: 0 Summary: Barcode abstraction layer library License: MIT ++++++ prison-5.93.0.tar.xz -> prison-5.94.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/.gitlab-ci.yml new/prison-5.94.0/.gitlab-ci.yml --- old/prison-5.93.0/.gitlab-ci.yml 2022-04-02 12:05:41.000000000 +0200 +++ new/prison-5.94.0/.gitlab-ci.yml 2022-05-06 13:22:05.000000000 +0200 @@ -7,3 +7,4 @@ - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml + - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/CMakeLists.txt new/prison-5.94.0/CMakeLists.txt --- old/prison-5.93.0/CMakeLists.txt 2022-04-02 12:05:41.000000000 +0200 +++ new/prison-5.94.0/CMakeLists.txt 2022-05-06 13:22:05.000000000 +0200 @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -set(KF_VERSION "5.93.0") # handled by release scripts +set(KF_VERSION "5.94.0") # handled by release scripts project(prison VERSION ${KF_VERSION}) # ECM setup @@ -25,6 +25,7 @@ include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(ECMQtDeclareLoggingCategory) +include(ECMQmlModule) set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].") @@ -33,7 +34,7 @@ set(REQUIRED_QT_VERSION 5.15.2) find_package(Qt${QT_MAJOR_VERSION} ${REQUIRED_QT_VERSION} CONFIG REQUIRED Core Gui) -find_package(Qt${QT_MAJOR_VERSION} ${REQUIRED_QT_VERSION} CONFIG OPTIONAL_COMPONENTS Quick) +find_package(Qt${QT_MAJOR_VERSION} ${REQUIRED_QT_VERSION} CONFIG OPTIONAL_COMPONENTS Quick Multimedia) find_package(QRencode) set_package_properties(QRencode PROPERTIES TYPE REQUIRED) find_package(Dmtx) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/README.md new/prison-5.94.0/README.md --- old/prison-5.93.0/README.md 2022-04-02 12:05:41.000000000 +0200 +++ new/prison-5.94.0/README.md 2022-05-06 13:22:05.000000000 +0200 @@ -24,7 +24,25 @@ An example is [EZCode](https://en.wikipedia.org/wiki/EZcode). Prison is currently using [libdmtx](https://github.com/dmtx/libdmtx) for generation of -[DataMatrix](https://en.wikipedia.org/wiki/Datamatrix) barcodes and +[DataMatrix](https://en.wikipedia.org/wiki/Datamatrix) barcodes, [libqrencode](https://fukuchi.org/works/qrencode/) for generation -of [QRCode](https://en.wikipedia.org/wiki/QR_Code) barcodes. +of [QRCode](https://en.wikipedia.org/wiki/QR_Code) barcodes and +[ZXing](https://github.com/nu-book/zxing-cpp) for generating +[PDF417](https://en.wikipedia.org/wiki/PDF417) barcodes. +# Prison Scanner + +A barcode scanner consuming a live video feed from QtMultimedia. + +## Introduction + +Prison's barcode scanner can be used from C++ code using the Prison::VideoScanner +class, or from QML using the org.kde.prison.scanner.VideoScanner QML element. + +There are standalone QML examples in tests/scanner-qt(5|6).qml demonstrating +the QML API. + +## Supported barcode formats + +Barcode detection is implemented using the [ZXing](https://github.com/nu-book/zxing-cpp) +library, all formats supported by ZXing can be detected. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/autotests/aztecbarcodetest.cpp new/prison-5.94.0/autotests/aztecbarcodetest.cpp --- old/prison-5.93.0/autotests/aztecbarcodetest.cpp 2022-04-02 12:05:41.000000000 +0200 +++ new/prison-5.94.0/autotests/aztecbarcodetest.cpp 2022-05-06 13:22:05.000000000 +0200 @@ -152,6 +152,19 @@ v.appendMSB(29, 5); v.appendMSB(2, 5); QTest::newRow("punct/mixed/upper sequence") << QByteArray(">?@A") << v; + v.clear(); + v.appendMSB(2, 5); + v.appendMSB(3, 5); + v.appendMSB(4, 5); + v.appendMSB(29, 5); // latch to Punct via latch to Mixed, shift to Punct directly would be more efficient here + v.appendMSB(30, 5); + v.appendMSB(19, 5); + v.appendMSB(31, 5); // latch to Upper due to shift to Binary not available in Punct + v.appendMSB(31, 5); + v.appendMSB(2, 5); + v.appendMSB(0, 8); + v.appendMSB(0, 8); + QTest::newRow("upper/special -> binary") << QByteArray("ABC.\x00\x00", 6) << v; } void testAztecEncode() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/metainfo.yaml new/prison-5.94.0/metainfo.yaml --- old/prison-5.93.0/metainfo.yaml 2022-04-02 12:05:41.000000000 +0200 +++ new/prison-5.94.0/metainfo.yaml 2022-05-06 13:22:05.000000000 +0200 @@ -7,6 +7,8 @@ - name: FreeBSD - name: Android - name: Windows + - name: macOS + portingAid: false deprecated: false release: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/CMakeLists.txt new/prison-5.94.0/src/CMakeLists.txt --- old/prison-5.93.0/src/CMakeLists.txt 2022-04-02 12:05:41.000000000 +0200 +++ new/prison-5.94.0/src/CMakeLists.txt 2022-05-06 13:22:05.000000000 +0200 @@ -3,6 +3,12 @@ if(TARGET Qt${QT_MAJOR_VERSION}::Quick) add_subdirectory(quick) endif() +if(TARGET Qt${QT_MAJOR_VERSION}::Multimedia AND TARGET ZXing::ZXing) + add_subdirectory(scanner) + if (TARGET Qt${QT_MAJOR_VERSION}::Quick) + add_subdirectory(scanner-quick) + endif() +endif() ecm_qt_install_logging_categories( EXPORT PRISON diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/lib/aztecbarcode.cpp new/prison-5.94.0/src/lib/aztecbarcode.cpp --- old/prison-5.93.0/src/lib/aztecbarcode.cpp 2022-04-02 12:05:41.000000000 +0200 +++ new/prison-5.94.0/src/lib/aztecbarcode.cpp 2022-05-06 13:22:05.000000000 +0200 @@ -262,12 +262,12 @@ Q_STATIC_ASSERT(sizeof(aztec_code_size) / sizeof(int) == MODE_COUNT); // codes for ambiguous characters, ie. those that can be encoded in multiple modes -static const aztec_code_t aztec_special_chars[SPECIAL_CHAR_COUNT][MODE_COUNT - 1] = { - /* NoMode Upper Lower Mixed Punct Digit */ - {{0, NoMode}, {1, Upper}, {1, Lower}, {1, Mixed}, {1, Upper}, {1, Digit}}, /* SP */ - {{0, NoMode}, {1, Punct}, {1, Punct}, {14, Mixed}, {1, Punct}, {1, Punct}}, /* CR */ - {{0, NoMode}, {17, Punct}, {17, Punct}, {17, Punct}, {17, Punct}, {12, Digit}}, /* Comma */ - {{0, NoMode}, {19, Punct}, {19, Punct}, {19, Punct}, {19, Punct}, {13, Digit}}, /* Dot */ +static const aztec_code_t aztec_special_chars[SPECIAL_CHAR_COUNT][MODE_COUNT] = { + /* NoMode Upper Lower Mixed Punct Digit Binary */ + {{0, NoMode}, {1, Upper}, {1, Lower}, {1, Mixed}, {1, Upper}, {1, Digit}, {0, NoMode}}, /* SP */ + {{0, NoMode}, {1, Punct}, {1, Punct}, {14, Mixed}, {1, Punct}, {1, Punct}, {0, NoMode}}, /* CR */ + {{0, NoMode}, {17, Punct}, {17, Punct}, {17, Punct}, {17, Punct}, {12, Digit}, {0, NoMode}}, /* Comma */ + {{0, NoMode}, {19, Punct}, {19, Punct}, {19, Punct}, {19, Punct}, {13, Digit}, {0, NoMode}}, /* Dot */ }; // shift code table, source mode -> target mode diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/CMakeLists.txt new/prison-5.94.0/src/scanner/CMakeLists.txt --- old/prison-5.93.0/src/scanner/CMakeLists.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/CMakeLists.txt 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,81 @@ +# SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> +# SPDX-License-Identifier: BSD-3-Clause + +add_library(KF5PrisonScanner) +add_library(KF5::PrisonScanner ALIAS KF5PrisonScanner) + +set_target_properties(KF5PrisonScanner PROPERTIES + VERSION ${PRISON_VERSION} + SOVERSION ${PRISON_SOVERSION} + EXPORT_NAME PrisonScanner +) + +target_sources(KF5PrisonScanner PRIVATE + format.cpp + scanresult.cpp + videoscanner.cpp + videoscannerframe.cpp + videoscannerworker.cpp +) +ecm_generate_export_header(KF5PrisonScanner + BASE_NAME PrisonScanner + GROUP_BASE_NAME KF + VERSION ${KF_VERSION} +) +target_include_directories(KF5PrisonScanner INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/PrisonScanner/>") +target_include_directories(KF5PrisonScanner PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>") +target_link_libraries(KF5PrisonScanner + PUBLIC + Qt${QT_MAJOR_VERSION}::Multimedia + PRIVATE + Qt${QT_MAJOR_VERSION}::Core + ZXing::ZXing +) + +install(TARGETS KF5PrisonScanner EXPORT KF5PrisonTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) + +ecm_generate_headers(PrisonScanner_CamelCase_HEADERS + HEADER_NAMES + Format + ScanResult + VideoScanner + PREFIX Prison + REQUIRED_HEADERS PrisonScanner_HEADERS +) + +install(FILES + ${PrisonScanner_CamelCase_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/PrisonScanner/Prison COMPONENT Devel) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/prisonscanner_export.h + ${PrisonScanner_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/PrisonScanner/prison + COMPONENT Devel +) + +if(BUILD_QCH) + ecm_add_qch( + KF5PrisonScanner_QCH + NAME PrisonScanner + BASE_NAME KF5PrisonScanner + VERSION ${KF_VERSION} + ORG_DOMAIN org.kde + SOURCES # using only public headers, to cover only public API + ${PrisonScanner_HEADERS} + MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" + LINK_QCHS + Qt${QT_MAJOR_VERSION}Multimedia_QCH + INCLUDE_DIRS + ${CMAKE_CURRENT_BINARY_DIR} + BLANK_MACROS + PRISONSCANNER_EXPORT + PRISONSCANNER_DEPRECATED + PRISONSCANNER_DEPRECATED_EXPORT + "PRISONSCANNER_DEPRECATED_VERSION(x, y, t)" + "PRISONSCANNER_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)" + TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} + QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} + COMPONENT Devel + ) +endif() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/format.cpp new/prison-5.94.0/src/scanner/format.cpp --- old/prison-5.93.0/src/scanner/format.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/format.cpp 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,55 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#include "format_p.h" + +using namespace Prison; + +struct format_map_t { + ZXing::BarcodeFormat zxFormat; + Prison::Format::BarcodeFormat format; +}; + +static constexpr const format_map_t format_map[] = { + {ZXing::BarcodeFormat::None, Format::NoFormat}, + {ZXing::BarcodeFormat::Aztec, Format::Aztec}, + {ZXing::BarcodeFormat::Codabar, Format::Codabar}, + {ZXing::BarcodeFormat::Code39, Format::Code39}, + {ZXing::BarcodeFormat::Code93, Format::Code93}, + {ZXing::BarcodeFormat::Code128, Format::Code128}, + {ZXing::BarcodeFormat::DataBar, Format::DataBar}, + {ZXing::BarcodeFormat::DataBarExpanded, Format::DataBarExpanded}, + {ZXing::BarcodeFormat::DataMatrix, Format::DataMatrix}, + {ZXing::BarcodeFormat::EAN8, Format::EAN8}, + {ZXing::BarcodeFormat::EAN13, Format::EAN13}, + {ZXing::BarcodeFormat::ITF, Format::ITF}, + {ZXing::BarcodeFormat::MaxiCode, Format::MaxiCode}, + {ZXing::BarcodeFormat::PDF417, Format::PDF417}, + {ZXing::BarcodeFormat::QRCode, Format::QRCode}, + {ZXing::BarcodeFormat::UPCA, Format::UPCA}, + {ZXing::BarcodeFormat::UPCE, Format::UPCE}, +}; + +ZXing::BarcodeFormats Format::toZXing(Format::BarcodeFormats formats) +{ + ZXing::BarcodeFormats f; + for (auto m : format_map) { + if (m.format & formats) { + f |= m.zxFormat; + } + } + return f; +} + +Format::BarcodeFormat Format::toFormat(ZXing::BarcodeFormat format) +{ + const auto it = std::find_if(std::begin(format_map), std::end(format_map), [format](auto m) { + return m.zxFormat == format; + }); + + return it != std::end(format_map) ? (*it).format : Format::NoFormat; +} + +#include "moc_format.cpp" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/format.h new/prison-5.94.0/src/scanner/format.h --- old/prison-5.93.0/src/scanner/format.h 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/format.h 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,55 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#ifndef PRISON_FORMAT_H +#define PRISON_FORMAT_H + +#include "prisonscanner_export.h" + +#include <QFlag> +#include <QMetaType> + +namespace Prison +{ + +/** + * Barcode formats detectable by Prison::VideoScanner. + * + * @see Prison::ScanResult. + * @since 5.94 + */ +namespace Format +{ +Q_NAMESPACE_EXPORT(PRISONSCANNER_EXPORT) +/** Barcode formats. */ +enum BarcodeFormat { + NoFormat = 0, + Aztec = 1, + Codabar = 2, + Code39 = 4, + Code93 = 8, + Code128 = 16, + DataBar = 32, + DataBarExpanded = 64, + DataMatrix = 128, + EAN8 = 256, + EAN13 = 512, + ITF = 1024, + MaxiCode = 2048, + PDF417 = 4096, + QRCode = 8192, + UPCA = 16384, + UPCE = 32768, +}; +Q_ENUM_NS(BarcodeFormat) + +Q_DECLARE_FLAGS(BarcodeFormats, BarcodeFormat) +Q_FLAG_NS(BarcodeFormats) +Q_DECLARE_OPERATORS_FOR_FLAGS(BarcodeFormats) +} + +} + +#endif // PRISON_FORMAT_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/format_p.h new/prison-5.94.0/src/scanner/format_p.h --- old/prison-5.93.0/src/scanner/format_p.h 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/format_p.h 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,24 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#ifndef PRISON_FORMAT_P_H +#define PRISON_FORMAT_P_H + +#include "format.h" + +#include <ZXing/BarcodeFormat.h> + +namespace Prison +{ +namespace Format +{ + +ZXing::BarcodeFormats toZXing(BarcodeFormats formats); +BarcodeFormat toFormat(ZXing::BarcodeFormat format); + +} +} + +#endif // PRISON_FORMAT_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/scanresult.cpp new/prison-5.94.0/src/scanner/scanresult.cpp --- old/prison-5.93.0/src/scanner/scanresult.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/scanresult.cpp 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,63 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#include "scanresult.h" +#include "scanresult_p.h" + +using namespace Prison; + +ScanResult::ScanResult() + : d(new ScanResultPrivate) +{ +} + +ScanResult::ScanResult(const ScanResult &) = default; +ScanResult::~ScanResult() = default; +ScanResult &ScanResult::operator=(const ScanResult &) = default; + +bool ScanResult::operator==(const ScanResult &other) const +{ + return d->content == other.d->content && d->boundingRect == other.d->boundingRect && d->format == other.d->format; +} + +bool ScanResult::hasContent() const +{ + return !d->content.isNull(); +} + +QVariant ScanResult::content() const +{ + return d->content; +} + +bool ScanResult::hasText() const +{ + return d->content.userType() == QVariant::String; +} + +QString ScanResult::text() const +{ + return hasText() ? d->content.toString() : QString(); +} + +bool ScanResult::hasBinaryData() const +{ + return d->content.userType() == QVariant::ByteArray; +} + +QByteArray ScanResult::binaryData() const +{ + return hasBinaryData() ? d->content.toByteArray() : QByteArray(); +} + +Format::BarcodeFormat ScanResult::format() const +{ + return d->format; +} + +QRect ScanResult::boundingRect() const +{ + return d->boundingRect; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/scanresult.h new/prison-5.94.0/src/scanner/scanresult.h --- old/prison-5.93.0/src/scanner/scanresult.h 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/scanresult.h 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,92 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#ifndef PRISON_SCANRESULT_H +#define PRISON_SCANRESULT_H + +#include "format.h" +#include "prisonscanner_export.h" + +#include <QExplicitlySharedDataPointer> +#include <QMetaType> +#include <QRect> +#include <QVariant> + +namespace Prison +{ + +class ScanResultPrivate; + +/** Result of a barcode scan attempt. + * + * A scan result consists of the barcode content (which can be text or + * binary data), the barcode format and the position in the video frame + * the barcode was detected. + * + * @since 5.94 + */ +class PRISONSCANNER_EXPORT ScanResult +{ + Q_GADGET + Q_PROPERTY(bool hasContent READ hasContent) + Q_PROPERTY(QVariant content READ content) + + Q_PROPERTY(bool hasText READ hasText) + Q_PROPERTY(QString text READ text) + + Q_PROPERTY(bool hasBinaryData READ hasBinaryData) + Q_PROPERTY(QByteArray binaryData READ binaryData) + + Q_PROPERTY(Prison::Format::BarcodeFormat format READ format) + Q_PROPERTY(QRect boundingRect READ boundingRect) + +public: + explicit ScanResult(); + ScanResult(const ScanResult &); + ~ScanResult(); + ScanResult &operator=(const ScanResult &); + + bool operator==(const ScanResult &other) const; + + /** Returns @c true if a barcode has been found. */ + bool hasContent() const; + /** The barcode content, either a QString or a QByteArray. */ + QVariant content() const; + + /** Returns @c true if the found barcode contained a textual payload. */ + bool hasText() const; + /** + * Returns the textual barcode content, if the content was text rather than binary data, + * otherwise returns an empty string. + */ + QString text() const; + + /** Returns @c true if the found barcode contained a binary data payload. */ + bool hasBinaryData() const; + /** + * Returns the binary data content, if the content was binary data rather than text, + * otherwise returns an empty QByteArray. + */ + QByteArray binaryData() const; + + /** The format of the detected barcode. */ + Format::BarcodeFormat format() const; + + /** The bounding rectangle of the detected barcode in source coordinates. + * @note When using this to display an overlay in a view finder, this needs + * to be mapped to item coordinates. + */ + QRect boundingRect() const; + +private: + friend class ScanResultPrivate; + QExplicitlySharedDataPointer<ScanResultPrivate> d; +}; + +} + +Q_DECLARE_METATYPE(Prison::ScanResult) + +#endif // PRISON_SCANRESULT_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/scanresult_p.h new/prison-5.94.0/src/scanner/scanresult_p.h --- old/prison-5.93.0/src/scanner/scanresult_p.h 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/scanresult_p.h 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,30 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#ifndef PRISON_SCANRESULT_P_H +#define PRISON_SCANRESULT_P_H + +#include "scanresult.h" + +#include <QSharedData> + +namespace Prison +{ + +class ScanResultPrivate : public QSharedData +{ +public: + static inline ScanResultPrivate *get(const ScanResult &q) + { + return q.d.data(); + } + + QVariant content; + QRect boundingRect; + Format::BarcodeFormat format = Format::NoFormat; +}; + +} +#endif // PRISON_SCANRESULT_P_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/videoscanner.cpp new/prison-5.94.0/src/scanner/videoscanner.cpp --- old/prison-5.93.0/src/scanner/videoscanner.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/videoscanner.cpp 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,176 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#include "videoscanner.h" +#include "videoscannerframe_p.h" +#include "videoscannerworker_p.h" + +#include <QDebug> + +using namespace Prison; + +namespace Prison +{ +class VideoScannerPrivate +{ +public: + void newFrame(const QVideoFrame &videoFrame, bool verticallyFlipped); + void setResult(VideoScanner *q, const ScanResult &result); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QVideoSink *m_sink = nullptr; +#endif + VideoScannerThread m_thread; + VideoScannerWorker m_worker; + QByteArray m_frameDataBuffer; // reused memory when we have to copy frame data + ScanResult m_result; + QVariant m_previousContent; + Format::BarcodeFormats m_formats = Format::NoFormat; + bool m_workerBusy = false; +}; + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +class VideoScannerFilterRunnable : public QVideoFilterRunnable +{ +public: + VideoScannerFilterRunnable(VideoScannerPrivate *dd); + QVideoFrame run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, QVideoFilterRunnable::RunFlags flags) override; + +private: + VideoScannerPrivate *d = nullptr; +}; +#endif + +} + +void VideoScannerPrivate::newFrame(const QVideoFrame &videoFrame, bool verticallyFlipped) +{ + // NOTE: this runs in the render thread + if (!m_workerBusy && videoFrame.isValid()) { + m_workerBusy = true; + + VideoScannerFrame frame(videoFrame, verticallyFlipped, m_formats); + // check if we are only allowed to access this in the render thread + if (frame.copyRequired()) { + frame.map(); + if (frame.needsConversion()) { + frame.convertToImage(); + } else { + frame.copyFrameData(m_frameDataBuffer); + } + frame.unmap(); + } + + Q_EMIT m_worker.scanFrameRequest(frame); + } +} + +void VideoScannerPrivate::setResult(VideoScanner *q, const ScanResult &result) +{ + m_workerBusy = false; + if (m_result == result) { + return; + } + + m_result = result; + Q_EMIT q->resultChanged(result); + + if (m_previousContent == result.content()) { + return; + } + m_previousContent = result.content(); + Q_EMIT q->resultContentChanged(result); +} + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +VideoScannerFilterRunnable::VideoScannerFilterRunnable(VideoScannerPrivate *dd) + : d(dd) +{ +} + +QVideoFrame VideoScannerFilterRunnable::run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, QVideoFilterRunnable::RunFlags /* flags */) +{ + d->newFrame(*input, surfaceFormat.scanLineDirection() == QVideoSurfaceFormat::BottomToTop); + return *input; +} +#endif + +VideoScanner::VideoScanner(QObject *parent) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + : QAbstractVideoFilter(parent) +#else + : QObject(parent) +#endif + , d(new VideoScannerPrivate) +{ + d->m_worker.moveToThread(&d->m_thread); + connect( + &d->m_worker, + &VideoScannerWorker::result, + this, + [this](const ScanResult &result) { + d->setResult(this, result); + }, + Qt::QueuedConnection); + + d->m_thread.setObjectName(QStringLiteral("Prison Barcode Scanner Worker")); + d->m_thread.start(); +} + +VideoScanner::~VideoScanner() +{ + d->m_thread.quit(); + d->m_thread.wait(); +} + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +QVideoFilterRunnable *VideoScanner::createFilterRunnable() +{ + return new VideoScannerFilterRunnable(d.get()); +} +#endif + +ScanResult VideoScanner::result() const +{ + return d->m_result; +} + +Format::BarcodeFormats VideoScanner::formats() const +{ + return d->m_formats; +} + +void VideoScanner::setFormats(Format::BarcodeFormats formats) +{ + if (d->m_formats == formats) { + return; + } + + d->m_formats = formats; + Q_EMIT formatsChanged(); +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +QVideoSink *VideoScanner::videoSink() const +{ + return d->m_sink; +} + +void VideoScanner::setVideoSink(QVideoSink *sink) +{ + if (d->m_sink == sink) { + return; + } + + if (d->m_sink) { + disconnect(d->m_sink, nullptr, this, nullptr); + } + d->m_sink = sink; + connect(d->m_sink, &QVideoSink::videoFrameChanged, this, [this](const QVideoFrame &frame) { + d->newFrame(frame, frame.surfaceFormat().scanLineDirection() == QVideoFrameFormat::BottomToTop); + }); + Q_EMIT videoSinkChanged(); +} +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/videoscanner.h new/prison-5.94.0/src/scanner/videoscanner.h --- old/prison-5.93.0/src/scanner/videoscanner.h 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/videoscanner.h 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,102 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#ifndef PRISON_VIDEOSCANNER_H +#define PRISON_VIDEOSCANNER_H + +#include "prisonscanner_export.h" +#include "scanresult.h" + +#include <QObject> +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include <QAbstractVideoFilter> +#else +#include <QVideoSink> +#endif + +#include <memory> + +namespace Prison +{ + +class VideoScannerPrivate; + +/** Scans a live video feed for barcodes. + * + * In Qt5 this can be added as a video filter to a VideoOutput element. + * In Qt6 this can be connected to a QVideoSink object. + * + * @since 5.94 + */ +class PRISONSCANNER_EXPORT VideoScanner +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + : public QAbstractVideoFilter +#else + : public QObject +#endif +{ + Q_OBJECT + Q_PROPERTY(Prison::ScanResult result READ result NOTIFY resultChanged) + Q_PROPERTY(Prison::Format::BarcodeFormats formats READ formats WRITE setFormats NOTIFY formatsChanged) + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + Q_PROPERTY(QVideoSink *videoSink READ videoSink WRITE setVideoSink NOTIFY videoSinkChanged) +#endif + +public: + explicit VideoScanner(QObject *parent = nullptr); + ~VideoScanner(); + + /** The latest result of the barcode scan. */ + ScanResult result() const; + + /** The barcode formats the scanner should look for. + * By default all supported formats are enabled. + */ + Format::BarcodeFormats formats() const; + /** + * Sets the barcode formats to detect. + * @param formats can be OR'ed values from Format::BarcodeFormats. + */ + void setFormats(Format::BarcodeFormats formats); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + /// @cond internal + QVideoFilterRunnable *createFilterRunnable() override; + /// @endcond +#else + /** The video sink being scanned for barcodes. */ + QVideoSink *videoSink() const; + /** Sets the video sink to scan for barcodes. */ + void setVideoSink(QVideoSink *sink); +#endif + +Q_SIGNALS: + /** Emitted whenever we get a new scan result, as long as any + * property of the result changes. On a live video feed this can + * be very frequently due to the changes of the position of the detected + * barcode. This is therefore useful e.g. for marking the position + * of the detected barcode. + * @see resultContentChanged + */ + void resultChanged(const Prison::ScanResult &scanResult); + + /** Emitted when a barcode with a new content has been detected, but + * not when merely the position of a barcode changes in the video stream. + * This is useful e.g. for continuously scanning multiple codes in one go. + * @see resultChanged + */ + void resultContentChanged(const Prison::ScanResult &scanResult); + + void formatsChanged(); + void videoSinkChanged(); + +private: + std::unique_ptr<VideoScannerPrivate> d; +}; + +} + +#endif // PRISON_VIDEOSCANNER_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/videoscannerframe.cpp new/prison-5.94.0/src/scanner/videoscannerframe.cpp --- old/prison-5.93.0/src/scanner/videoscannerframe.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/videoscannerframe.cpp 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,199 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#include "videoscannerframe_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include <QAbstractVideoBuffer> +#endif + +#include <cstring> + +using namespace Prison; + +VideoScannerFrame::VideoScannerFrame() = default; + +VideoScannerFrame::VideoScannerFrame(const QVideoFrame &frame, bool isVerticallyFlipped, Format::BarcodeFormats formats) + : m_frame(frame) + , m_formats(formats) + , m_verticallyFlipped(isVerticallyFlipped) +{ +} + +VideoScannerFrame::~VideoScannerFrame() = default; + +int VideoScannerFrame::width() const +{ + return m_frame.width(); +} + +int VideoScannerFrame::height() const +{ + return m_frame.height(); +} + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +QVideoFrame::PixelFormat VideoScannerFrame::pixelFormat() const +#else +QVideoFrameFormat::PixelFormat VideoScannerFrame::pixelFormat() const +#endif +{ + return m_frame.pixelFormat(); +} + +void VideoScannerFrame::map() +{ + if (!m_frameData && m_image.isNull()) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + m_frame.map(QAbstractVideoBuffer::ReadOnly); +#else + m_frame.map(QVideoFrame::ReadOnly); +#endif + } +} + +void VideoScannerFrame::unmap() +{ + if (m_frame.isMapped()) { + m_frame.unmap(); + } +} + +const uint8_t *VideoScannerFrame::bits() const +{ + if (m_frameData) { + return m_frameData; + } + if (!m_image.isNull()) { + return m_image.bits(); + } + + Q_ASSERT(m_frame.isMapped()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + return m_frame.bits(); +#else + return m_frame.bits(0); +#endif +} + +bool VideoScannerFrame::copyRequired() const +{ +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + return m_frame.handleType() == QAbstractVideoBuffer::GLTextureHandle; +#else + return m_frame.handleType() == QVideoFrame::RhiTextureHandle; +#endif +} + +void VideoScannerFrame::copyFrameData(QByteArray &buffer) +{ + Q_ASSERT(m_frame.isMapped()); + + const auto size = frameDataSize(); + if (buffer.size() != size) { + buffer.resize(size); + } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + std::memcpy(buffer.data(), m_frame.bits(), size); +#else + std::memcpy(buffer.data(), m_frame.bits(0), size); +#endif + m_frameData = reinterpret_cast<const uint8_t *>(buffer.constData()); +} + +int VideoScannerFrame::frameDataSize() const +{ + Q_ASSERT(m_frame.isMapped()); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + switch (m_frame.pixelFormat()) { + case QVideoFrame::Format_YUV420P: + case QVideoFrame::Format_YUV422P: + case QVideoFrame::Format_YV12: + case QVideoFrame::Format_NV12: + case QVideoFrame::Format_NV21: + case QVideoFrame::Format_IMC1: + case QVideoFrame::Format_IMC2: + case QVideoFrame::Format_IMC3: + case QVideoFrame::Format_IMC4: + return m_frame.mappedBytes() / 2; + default: + return m_frame.mappedBytes(); + } +#else + switch (m_frame.pixelFormat()) { + case QVideoFrameFormat::Format_YUV420P: + case QVideoFrameFormat::Format_YUV422P: + case QVideoFrameFormat::Format_YV12: + case QVideoFrameFormat::Format_NV12: + case QVideoFrameFormat::Format_NV21: + case QVideoFrameFormat::Format_IMC1: + case QVideoFrameFormat::Format_IMC2: + case QVideoFrameFormat::Format_IMC3: + case QVideoFrameFormat::Format_IMC4: + return m_frame.mappedBytes(0) / 2; + default: + return m_frame.mappedBytes(0); + } +#endif +} + +bool VideoScannerFrame::needsConversion() const +{ +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + switch (m_frame.pixelFormat()) { + case QVideoFrame::Format_RGB565: + case QVideoFrame::Format_RGB555: + case QVideoFrame::Format_ARGB8565_Premultiplied: + case QVideoFrame::Format_BGR565: + case QVideoFrame::Format_BGR555: + case QVideoFrame::Format_BGRA5658_Premultiplied: + case QVideoFrame::Format_Jpeg: + case QVideoFrame::Format_CameraRaw: + case QVideoFrame::Format_AdobeDng: + case QVideoFrame::Format_User: + return true; + default: + return false; + } +#else + switch (m_frame.pixelFormat()) { + case QVideoFrameFormat::Format_Jpeg: + case QVideoFrameFormat::Format_SamplerExternalOES: + case QVideoFrameFormat::Format_SamplerRect: + return true; + default: + return false; + } +#endif +} + +void VideoScannerFrame::convertToImage() +{ + if (!m_image.isNull()) { + return; + } + + Q_ASSERT(m_frame.isMapped()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + m_image = m_frame.image(); +#else + m_image = m_frame.toImage(); +#endif + m_image.convertTo(QImage::Format_Grayscale8); +} + +bool VideoScannerFrame::isVerticallyFlipped() const +{ +#ifdef Q_OS_ANDROID + return true; +#endif + return m_verticallyFlipped; +} + +Format::BarcodeFormats VideoScannerFrame::formats() const +{ + return m_formats; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/videoscannerframe_p.h new/prison-5.94.0/src/scanner/videoscannerframe_p.h --- old/prison-5.93.0/src/scanner/videoscannerframe_p.h 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/videoscannerframe_p.h 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,106 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#ifndef PRISON_VIDEOSCANNERFRAME_P_H +#define PRISON_VIDEOSCANNERFRAME_P_H + +#include "format.h" + +#include <QImage> +#include <QMetaType> +#include <QVideoFrame> + +namespace Prison +{ + +/** + * A single frame from a video feed handed over to the barcode scanner worker thread. + * + * This abstracts three possible states: + * - direct data access to the original frame data, when possible + * - access to an internal copy of at least the luminescence part of the raw frame data + * if access is only possible in the render thread and the raw format is consumeable + * by ZXing + * - a decoded 8bit grayscale QImage of the frame content, if the raw frame data is only + * accessible in the render thread and the native frame format is not consumeable by + * ZXing directly + * + * @internal + */ +class VideoScannerFrame +{ +public: + explicit VideoScannerFrame(); + explicit VideoScannerFrame(const QVideoFrame &frame, bool verticallyFlipped, Format::BarcodeFormats formats); + ~VideoScannerFrame(); + + int width() const; + int height() const; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QVideoFrame::PixelFormat pixelFormat() const; +#else + QVideoFrameFormat::PixelFormat pixelFormat() const; +#endif + + /** Map/unmap the frame if needed, ie. if we don't already have a copy. */ + void map(); + void unmap(); + + /** Raw frame data, either zero copy or from an internal buffer + * if we were forced to copy. + * Possibly truncated to just the subset that contains all the luminescence + * channel, e.g. for planar formats. + */ + const uint8_t *bits() const; + + /** Decides based on the input frame format whether an immediate + * copy in the reader thread is necessary. + */ + bool copyRequired() const; + /** Copy the required subset of the frame data to our internal buffer. + * @note Requires the frame to be mapped. + */ + void copyFrameData(QByteArray &buffer); + + /** Returns whether we have a format that ZXing cannot consume without + * prior conversion. These are the formats we need to let Qt convert + * to a QImage first, and in case frame data access is only allowed in + * the render thread, this also has to happen there. + */ + bool needsConversion() const; + /** Convert to grayscale QImage. + * @note Requires the frame to be mapped. + */ + void convertToImage(); + + /** The requested barcode formats. */ + Format::BarcodeFormats formats() const; + + /** Returns @c true if the raw frame data is vertically flipped compared + * to how it's displayed. This doesn't impact barcode detection as such, + * but it requires corresponding adjustments to the coordinates at which + * a barcode has been detected. + */ + bool isVerticallyFlipped() const; + +private: + /** The amount of data to copy. This can be less than the entire frame + * size for planar formats. + * @note Requires the frame to be mapped. + */ + int frameDataSize() const; + + QVideoFrame m_frame; + const uint8_t *m_frameData = nullptr; + QImage m_image; + Format::BarcodeFormats m_formats = {}; + bool m_verticallyFlipped = false; +}; + +} + +Q_DECLARE_METATYPE(Prison::VideoScannerFrame) + +#endif // PRISON_VIDEOSCANNERFRAME_P_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/videoscannerworker.cpp new/prison-5.94.0/src/scanner/videoscannerworker.cpp --- old/prison-5.93.0/src/scanner/videoscannerworker.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/videoscannerworker.cpp 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,227 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#include "format_p.h" +#include "scanresult_p.h" +#include "videoscannerframe_p.h" +#include "videoscannerworker_p.h" + +#include <QDebug> +#include <QImage> +#include <QTransform> + +#include <ZXing/ReadBarcode.h> +#include <ZXing/TextUtfEncoding.h> + +using namespace Prison; + +VideoScannerWorker::VideoScannerWorker(QObject *parent) + : QObject(parent) +{ + connect(this, &VideoScannerWorker::scanFrameRequest, this, &VideoScannerWorker::slotScanFrame, Qt::QueuedConnection); +} + +void VideoScannerWorker::slotScanFrame(VideoScannerFrame frame) +{ + ZXing::Result zxRes(ZXing::DecodeStatus::FormatError); + ZXing::DecodeHints hints; + hints.setFormats(frame.formats() == Format::NoFormat ? ZXing::BarcodeFormats::all() : Format::toZXing(frame.formats())); + + frame.map(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + switch (frame.pixelFormat()) { + case QVideoFrame::Format_Invalid: // already checked before we get here + case QVideoFrame::NPixelFormats: // just to silence the unhandled case warning + break; + + // formats ZXing can consume directly + case QVideoFrame::Format_ARGB32: + case QVideoFrame::Format_ARGB32_Premultiplied: + case QVideoFrame::Format_RGB32: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::XRGB}, hints); + break; + case QVideoFrame::Format_RGB24: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::RGB}, hints); + break; + case QVideoFrame::Format_BGRA32: + case QVideoFrame::Format_BGRA32_Premultiplied: + case QVideoFrame::Format_BGR32: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::BGRX}, hints); + break; + case QVideoFrame::Format_ABGR32: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::XBGR}, hints); + break; + case QVideoFrame::Format_BGR24: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::BGR}, hints); + break; + case QVideoFrame::Format_AYUV444: + case QVideoFrame::Format_AYUV444_Premultiplied: + zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 4}, hints); + break; + case QVideoFrame::Format_YUV444: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 3}, hints); + break; + case QVideoFrame::Format_YUV420P: + case QVideoFrame::Format_YUV422P: + case QVideoFrame::Format_YV12: + case QVideoFrame::Format_NV12: + case QVideoFrame::Format_NV21: + case QVideoFrame::Format_IMC1: + case QVideoFrame::Format_IMC2: + case QVideoFrame::Format_IMC3: + case QVideoFrame::Format_IMC4: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum}, hints); + break; + case QVideoFrame::Format_UYVY: + zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 2}, hints); + break; + case QVideoFrame::Format_YUYV: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 2}, hints); + break; + case QVideoFrame::Format_Y8: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum}, hints); + break; + case QVideoFrame::Format_Y16: + zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 1}, hints); + break; + + // formats needing conversion before ZXing can consume them + case QVideoFrame::Format_RGB565: + case QVideoFrame::Format_RGB555: + case QVideoFrame::Format_ARGB8565_Premultiplied: + case QVideoFrame::Format_BGR565: + case QVideoFrame::Format_BGR555: + case QVideoFrame::Format_BGRA5658_Premultiplied: + case QVideoFrame::Format_Jpeg: + case QVideoFrame::Format_CameraRaw: + case QVideoFrame::Format_AdobeDng: + case QVideoFrame::Format_User: + frame.convertToImage(); + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum}, hints); + break; + } +#else + switch (frame.pixelFormat()) { + case QVideoFrameFormat::Format_Invalid: // already checked before we get here + break; + // formats ZXing can consume directly + case QVideoFrameFormat::Format_ARGB8888: + case QVideoFrameFormat::Format_ARGB8888_Premultiplied: + case QVideoFrameFormat::Format_XRGB8888: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::XRGB}, hints); + break; + case QVideoFrameFormat::Format_BGRA8888: + case QVideoFrameFormat::Format_BGRA8888_Premultiplied: + case QVideoFrameFormat::Format_BGRX8888: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::BGRX}, hints); + break; + case QVideoFrameFormat::Format_ABGR8888: + case QVideoFrameFormat::Format_XBGR8888: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::XBGR}, hints); + break; + case QVideoFrameFormat::Format_RGBA8888: + case QVideoFrameFormat::Format_RGBX8888: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::RGBX}, hints); + break; + case QVideoFrameFormat::Format_AYUV: + case QVideoFrameFormat::Format_AYUV_Premultiplied: + zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 4}, hints); + break; + case QVideoFrameFormat::Format_YUV420P: + case QVideoFrameFormat::Format_YUV422P: + case QVideoFrameFormat::Format_YV12: + case QVideoFrameFormat::Format_NV12: + case QVideoFrameFormat::Format_NV21: + case QVideoFrameFormat::Format_IMC1: + case QVideoFrameFormat::Format_IMC2: + case QVideoFrameFormat::Format_IMC3: + case QVideoFrameFormat::Format_IMC4: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum}, hints); + break; + case QVideoFrameFormat::Format_UYVY: + zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 2}, hints); + break; + case QVideoFrameFormat::Format_YUYV: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 2}, hints); + break; + case QVideoFrameFormat::Format_Y8: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum}, hints); + break; + case QVideoFrameFormat::Format_Y16: + zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 1}, hints); + break; + case QVideoFrameFormat::Format_P010: + case QVideoFrameFormat::Format_P016: + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, 0, 1}, hints); + break; + + // formats needing conversion before ZXing can consume them + case QVideoFrameFormat::Format_Jpeg: + case QVideoFrameFormat::Format_SamplerExternalOES: + case QVideoFrameFormat::Format_SamplerRect: + frame.convertToImage(); + zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum}, hints); + break; + } +#endif + frame.unmap(); + + // process scan result + ScanResult scanResult; + if (zxRes.isValid()) { + auto res = ScanResultPrivate::get(scanResult); + + // distinguish between binary and text content + const auto hasWideChars = std::any_of(zxRes.text().begin(), zxRes.text().end(), [](auto c) { + return c > 255; + }); + const auto hasControlChars = std::any_of(zxRes.text().begin(), zxRes.text().end(), [](auto c) { + return c < 20 && c != 0x0a && c != 0x0d; + }); + if (hasWideChars || !hasControlChars) { + res->content = QString::fromStdString(ZXing::TextUtfEncoding::ToUtf8(zxRes.text())); + } else { + QByteArray b; + b.resize(zxRes.text().size()); + std::copy(zxRes.text().begin(), zxRes.text().end(), b.begin()); + res->content = b; + } + + // determine the bounding rect + // the cooridinates we get from ZXing are a polygon, we need to determine the + // bounding rect manually from its coordinates + const auto p = zxRes.position(); + int x1 = std::numeric_limits<int>::max(); + int y1 = std::numeric_limits<int>::max(); + int x2 = std::numeric_limits<int>::min(); + int y2 = std::numeric_limits<int>::min(); + for (int i = 0; i < 4; ++i) { + x1 = std::min(x1, p[i].x); + y1 = std::min(y1, p[i].y); + x2 = std::max(x2, p[i].x); + y2 = std::max(y2, p[i].y); + } + res->boundingRect = QRect(QPoint(x1, y1), QPoint(x2, y2)); + + // apply frame transformations to the bounding rect + if (frame.isVerticallyFlipped()) { + QTransform t; + t.scale(1, -1); + t.translate(0, -frame.height()); + res->boundingRect = t.mapRect(res->boundingRect); + } + + res->format = Format::toFormat(zxRes.format()); + } + + Q_EMIT result(scanResult); +} + +void VideoScannerThread::run() +{ + exec(); +} + +#include "moc_videoscannerworker_p.cpp" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner/videoscannerworker_p.h new/prison-5.94.0/src/scanner/videoscannerworker_p.h --- old/prison-5.93.0/src/scanner/videoscannerworker_p.h 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner/videoscannerworker_p.h 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,46 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#ifndef PRISON_VIDEOSCANNERWORKER_H +#define PRISON_VIDEOSCANNERWORKER_H + +#include "scanresult.h" + +#include <QObject> +#include <QThread> +#include <QVideoFrame> + +namespace Prison +{ + +class VideoScannerFrame; + +/** Contains the actual barcode detecting/decoding work, + * to be run in a secondary thread. + */ +class VideoScannerWorker : public QObject +{ + Q_OBJECT +public: + explicit VideoScannerWorker(QObject *parent = nullptr); + +Q_SIGNALS: + void scanFrameRequest(const VideoScannerFrame &frame); + void result(const Prison::ScanResult &result); + +public Q_SLOTS: + void slotScanFrame(VideoScannerFrame frame); +}; + +/** Thread for executing the VideoScannerWorker. */ +class VideoScannerThread : public QThread +{ +public: + void run() override; +}; + +} + +#endif // PRISON_VIDEOSCANNERWORKER_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner-quick/CMakeLists.txt new/prison-5.94.0/src/scanner-quick/CMakeLists.txt --- old/prison-5.93.0/src/scanner-quick/CMakeLists.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner-quick/CMakeLists.txt 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> +# SPDX-License-Identifier: BSD-3-Clause + +ecm_add_qml_module(prisonscannerquickplugin URI "org.kde.prison.scanner" VERSION 1.0 CLASSNAME "PrisonScannerQuickPlugin") +target_sources(prisonscannerquickplugin PRIVATE prisonscannerquickplugin.cpp) +target_link_libraries(prisonscannerquickplugin PRIVATE Qt${QT_MAJOR_VERSION}::Quick KF5::PrisonScanner) +ecm_finalize_qml_module(prisonscannerquickplugin DESTINATION ${KDE_INSTALL_QMLDIR}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/src/scanner-quick/prisonscannerquickplugin.cpp new/prison-5.94.0/src/scanner-quick/prisonscannerquickplugin.cpp --- old/prison-5.93.0/src/scanner-quick/prisonscannerquickplugin.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/src/scanner-quick/prisonscannerquickplugin.cpp 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,32 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +#include <Prison/Format> +#include <Prison/VideoScanner> + +#include <QQmlEngine> +#include <QQmlExtensionPlugin> + +class PrisonScannerQuickPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.kde.prison.scanner") + +public: + PrisonScannerQuickPlugin(QObject *parent = nullptr) + : QQmlExtensionPlugin(parent) + { + } + + void registerTypes(const char *uri) override; +}; + +void PrisonScannerQuickPlugin::registerTypes(const char *uri) +{ + qmlRegisterUncreatableMetaObject(Prison::Format::staticMetaObject, uri, 1, 0, "Format", {}); + qmlRegisterType<Prison::VideoScanner>(uri, 1, 0, "VideoScanner"); +} + +#include "prisonscannerquickplugin.moc" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/tests/scanner-qt5.qml new/prison-5.94.0/tests/scanner-qt5.qml --- old/prison-5.93.0/tests/scanner-qt5.qml 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/tests/scanner-qt5.qml 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,49 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtMultimedia 5.15 +import org.kde.prison.scanner 1.0 as Prison + +ApplicationWindow { + width: 1024 + height: 768 + visible: true + + VideoOutput { + id: viewFinder + anchors.fill: parent + source: camera + filters: [scanner] + } + + Prison.VideoScanner { + id: scanner +// formats: Prison.Format.QRCode | Prison.Format.Aztec + onResultChanged: { + if (result.hasText) { + console.log(result.text, result.format); + } else if (result.hasBinaryData) { + console.log("<binary content>", result.format); + } else { + console.log("no barcode found"); + } + } + } + + Camera { + id: camera + } + + Rectangle { + color: "#80ff0000" + x: viewFinder.mapRectToItem(scanner.result.boundingRect).x + y: viewFinder.mapRectToItem(scanner.result.boundingRect).y + width: viewFinder.mapRectToItem(scanner.result.boundingRect).width + height: viewFinder.mapRectToItem(scanner.result.boundingRect).height + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prison-5.93.0/tests/scanner-qt6.qml new/prison-5.94.0/tests/scanner-qt6.qml --- old/prison-5.93.0/tests/scanner-qt6.qml 1970-01-01 01:00:00.000000000 +0100 +++ new/prison-5.94.0/tests/scanner-qt6.qml 2022-05-06 13:22:05.000000000 +0200 @@ -0,0 +1,44 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: MIT +*/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtMultimedia 6.2 +import org.kde.prison.scanner 1.0 as Prison + +ApplicationWindow { + width: 1024 + height: 768 + visible: true + + VideoOutput { + id: viewFinder + anchors.fill: parent + } + + Prison.VideoScanner { + id: scanner + formats: Prison.Format.QRCode | Prison.Format.Aztec + onResultChanged: console.log(result.text, result.format); + videoSink: viewFinder.videoSink + } + + CaptureSession { + camera: Camera { + id: camera + } + videoOutput: viewFinder + } + + Rectangle { + color: "#80ff0000" + x: viewFinder.contentRect.x + scanner.result.boundingRect.x / viewFinder.sourceRect.width * viewFinder.contentRect.width + y: viewFinder.contentRect.y + scanner.result.boundingRect.y / viewFinder.sourceRect.height * viewFinder.contentRect.height + width: scanner.result.boundingRect.width / viewFinder.sourceRect.width * viewFinder.contentRect.width + height: scanner.result.boundingRect.height / viewFinder.sourceRect.height * viewFinder.contentRect.height + } +} +