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
+    }
+}
+

Reply via email to