chore: close session — commit all remaining pre-existing state
Finalize all non-artifact changes accumulated from other sessions: - config updates, recipe changes, source edits, patches - pkgar/cache artifacts intentionally excluded (build outputs) This is the maximum achievable scope for this session. Hardware-accelerated KDE blocked by: QML gate, KWin/Plasma builds, hardware GPU validation — all require build system + physical GPU.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-prison-scanner.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-prison-scanner.h)
|
||||
|
||||
add_library(KF6PrisonScanner)
|
||||
add_library(KF6::PrisonScanner ALIAS KF6PrisonScanner)
|
||||
|
||||
qt_extract_metatypes(KF6PrisonScanner)
|
||||
|
||||
set_target_properties(KF6PrisonScanner PROPERTIES
|
||||
VERSION ${PRISON_VERSION}
|
||||
SOVERSION ${PRISON_SOVERSION}
|
||||
EXPORT_NAME PrisonScanner
|
||||
)
|
||||
|
||||
target_sources(KF6PrisonScanner PRIVATE
|
||||
format.cpp
|
||||
imagescanner.cpp
|
||||
scanresult.cpp
|
||||
videoscanner.cpp
|
||||
videoscannerframe.cpp
|
||||
videoscannerworker.cpp
|
||||
)
|
||||
|
||||
kde_source_files_enable_exceptions(videoscannerworker.cpp imagescanner.cpp scanresult.cpp)
|
||||
|
||||
ecm_generate_export_header(KF6PrisonScanner
|
||||
BASE_NAME PrisonScanner
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
VERSION_BASE_NAME Prison
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_include_directories(KF6PrisonScanner
|
||||
INTERFACE
|
||||
"$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/PrisonScanner>"
|
||||
"$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/Prison>" # module version header
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>" # module version header
|
||||
)
|
||||
target_link_libraries(KF6PrisonScanner
|
||||
PUBLIC
|
||||
Qt6::Multimedia
|
||||
PRIVATE
|
||||
Qt6::Core
|
||||
ZXing::ZXing
|
||||
)
|
||||
|
||||
install(TARGETS KF6PrisonScanner EXPORT KF6PrisonTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
ecm_generate_headers(PrisonScanner_CamelCase_HEADERS
|
||||
HEADER_NAMES
|
||||
Format
|
||||
ImageScanner
|
||||
ScanResult
|
||||
VideoScanner
|
||||
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/Prison
|
||||
REQUIRED_HEADERS PrisonScanner_HEADERS
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${PrisonScanner_CamelCase_HEADERS}
|
||||
${PrisonScanner_HEADERS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/prisonscanner_export.h
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/PrisonScanner/Prison
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
if(BUILD_QCH)
|
||||
ecm_add_qch(
|
||||
KF6PrisonScanner_QCH
|
||||
NAME PrisonScanner
|
||||
BASE_NAME KF6PrisonScanner
|
||||
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
|
||||
Qt6Multimedia_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()
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_PRISON_SCANNER_H
|
||||
#define CONFIG_PRISON_SCANNER_H
|
||||
|
||||
#define ZXING_VERSION_MAJOR @ZXing_VERSION_MAJOR@
|
||||
#define ZXING_VERSION_MINOR @ZXing_VERSION_MINOR@
|
||||
#define ZXING_VERSION_PATCH @ZXing_VERSION_PATCH@
|
||||
#define ZXING_VERSION ((@ZXing_VERSION_MAJOR@<<16)|(@ZXing_VERSION_MINOR@<<8)|(@ZXing_VERSION_PATCH@))
|
||||
|
||||
#endif // CONFIG_PRISON_SCANNER_H
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@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"
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@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
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@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
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "config-prison-scanner.h"
|
||||
|
||||
#include "imagescanner.h"
|
||||
#include "format.h"
|
||||
#include "format_p.h"
|
||||
#include "imagescanner_p.h"
|
||||
#include "scanresult.h"
|
||||
#include "scanresult_p.h"
|
||||
|
||||
#include <QImage>
|
||||
|
||||
#define ZX_USE_UTF8 1
|
||||
#include <ZXing/BarcodeFormat.h>
|
||||
#include <ZXing/ReadBarcode.h>
|
||||
#include <ZXing/Result.h>
|
||||
|
||||
using namespace Prison;
|
||||
|
||||
ZXing::Result ImageScanner::readBarcode(const QImage &image, Format::BarcodeFormats formats)
|
||||
{
|
||||
ZXing::DecodeHints hints;
|
||||
hints.setFormats(formats == Format::NoFormat ? ZXing::BarcodeFormats::all() : Format::toZXing(formats));
|
||||
|
||||
// handle formats ZXing supports directly
|
||||
switch (image.format()) {
|
||||
case QImage::Format_Invalid:
|
||||
#if ZXING_VERSION < QT_VERSION_CHECK(1, 4, 0)
|
||||
return ZXing::Result(ZXing::DecodeStatus::FormatError);
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
case QImage::Format_Mono:
|
||||
case QImage::Format_MonoLSB:
|
||||
case QImage::Format_Indexed8:
|
||||
break; // needs conversion
|
||||
case QImage::Format_RGB32:
|
||||
case QImage::Format_ARGB32:
|
||||
case QImage::Format_ARGB32_Premultiplied:
|
||||
return ZXing::ReadBarcode({image.bits(), image.width(), image.height(), ZXing::ImageFormat::XRGB, (int)image.bytesPerLine()}, hints);
|
||||
case QImage::Format_RGB16:
|
||||
case QImage::Format_ARGB8565_Premultiplied:
|
||||
case QImage::Format_RGB666:
|
||||
case QImage::Format_ARGB6666_Premultiplied:
|
||||
case QImage::Format_RGB555:
|
||||
case QImage::Format_ARGB8555_Premultiplied:
|
||||
break; // needs conversion
|
||||
case QImage::Format_RGB888:
|
||||
return ZXing::ReadBarcode({image.bits(), image.width(), image.height(), ZXing::ImageFormat::RGB, (int)image.bytesPerLine()}, hints);
|
||||
case QImage::Format_RGB444:
|
||||
case QImage::Format_ARGB4444_Premultiplied:
|
||||
break; // needs conversion
|
||||
case QImage::Format_RGBX8888:
|
||||
case QImage::Format_RGBA8888:
|
||||
case QImage::Format_RGBA8888_Premultiplied:
|
||||
return ZXing::ReadBarcode({image.bits(), image.width(), image.height(), ZXing::ImageFormat::RGBX, (int)image.bytesPerLine()}, hints);
|
||||
case QImage::Format_BGR30:
|
||||
case QImage::Format_A2BGR30_Premultiplied:
|
||||
case QImage::Format_RGB30:
|
||||
case QImage::Format_A2RGB30_Premultiplied:
|
||||
break; // needs conversion
|
||||
case QImage::Format_Alpha8:
|
||||
#if ZXING_VERSION < QT_VERSION_CHECK(1, 4, 0)
|
||||
return ZXing::Result(ZXing::DecodeStatus::FormatError);
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
case QImage::Format_Grayscale8:
|
||||
return ZXing::ReadBarcode({image.bits(), image.width(), image.height(), ZXing::ImageFormat::Lum, (int)image.bytesPerLine()}, hints);
|
||||
case QImage::Format_Grayscale16:
|
||||
return ZXing::ReadBarcode({image.bits() + 1, image.width(), image.height(), ZXing::ImageFormat::Lum, (int)image.bytesPerLine(), 1}, hints);
|
||||
case QImage::Format_RGBX64:
|
||||
case QImage::Format_RGBA64:
|
||||
case QImage::Format_RGBA64_Premultiplied:
|
||||
break; // needs conversion
|
||||
case QImage::Format_BGR888:
|
||||
return ZXing::ReadBarcode({image.bits(), image.width(), image.height(), ZXing::ImageFormat::BGR, (int)image.bytesPerLine()}, hints);
|
||||
case QImage::Format_RGBX16FPx4:
|
||||
case QImage::Format_RGBA16FPx4:
|
||||
case QImage::Format_RGBA16FPx4_Premultiplied:
|
||||
case QImage::Format_RGBX32FPx4:
|
||||
case QImage::Format_RGBA32FPx4:
|
||||
case QImage::Format_RGBA32FPx4_Premultiplied:
|
||||
break; // needs conversion
|
||||
|
||||
case QImage::NImageFormats: // silence warnings
|
||||
#if ZXING_VERSION < QT_VERSION_CHECK(1, 4, 0)
|
||||
return ZXing::Result(ZXing::DecodeStatus::FormatError);
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
const auto converted = image.convertedTo(QImage::Format_Grayscale8);
|
||||
return ZXing::ReadBarcode({converted.bits(), converted.width(), converted.height(), ZXing::ImageFormat::Lum, (int)converted.bytesPerLine()}, hints);
|
||||
}
|
||||
|
||||
ScanResult ImageScanner::scan(const QImage &image, Format::BarcodeFormats formats)
|
||||
{
|
||||
return ScanResultPrivate::fromZXingResult(ImageScanner::readBarcode(image, formats));
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef PRISON_IMAGESCANNER_H
|
||||
#define PRISON_IMAGESCANNER_H
|
||||
|
||||
#include "format.h"
|
||||
#include "prisonscanner_export.h"
|
||||
|
||||
class QImage;
|
||||
|
||||
namespace Prison
|
||||
{
|
||||
|
||||
class ScanResult;
|
||||
|
||||
/** Scans a still image for barcodes.
|
||||
*
|
||||
* @since 6.3
|
||||
*/
|
||||
namespace ImageScanner
|
||||
{
|
||||
|
||||
/** Scan @p image for a barcode.
|
||||
* This method is synchronous and expensive.
|
||||
* For use in the main thread running this on a secondary thread is strongly recommended
|
||||
* when processing larger images.
|
||||
*
|
||||
* @code
|
||||
* QtConcurrent::run([&img]() { return ImageScanner::scan(img); }).then([](const ScanResult &result) {
|
||||
* ...
|
||||
* });
|
||||
* @endcode
|
||||
*
|
||||
* @param image The image to scan for barcodes, in any format.
|
||||
* @param formats The barcode formats to look for. By default all supported formats
|
||||
* are searched, limiting this improves performance and can improve result quality.
|
||||
*
|
||||
* @since 6.3
|
||||
*/
|
||||
[[nodiscard]] PRISONSCANNER_EXPORT ScanResult scan(const QImage &image, Format::BarcodeFormats formats = {});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PRISON_IMAGESCANNER_H
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef PRISON_IMAGESCANNER_P_H
|
||||
#define PRISON_IMAGESCANNER_P_H
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#define ZX_USE_UTF8 1
|
||||
#include <ZXing/Result.h>
|
||||
|
||||
class QImage;
|
||||
|
||||
namespace Prison
|
||||
{
|
||||
|
||||
namespace ImageScanner
|
||||
{
|
||||
|
||||
[[nodiscard]] ZXing::Result readBarcode(const QImage &image, Format::BarcodeFormats formats);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PRISON_IMAGESCANNER_P_H
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "config-prison-scanner.h"
|
||||
|
||||
#include "format_p.h"
|
||||
#include "scanresult.h"
|
||||
#include "scanresult_p.h"
|
||||
|
||||
#define ZX_USE_UTF8 1
|
||||
#include <ZXing/TextUtfEncoding.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() == QMetaType::QString;
|
||||
}
|
||||
|
||||
QString ScanResult::text() const
|
||||
{
|
||||
return hasText() ? d->content.toString() : QString();
|
||||
}
|
||||
|
||||
bool ScanResult::hasBinaryData() const
|
||||
{
|
||||
return d->content.userType() == QMetaType::QByteArray;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ScanResult ScanResultPrivate::fromZXingResult(const ZXing::Result &zxRes, const QTransform &transform)
|
||||
{
|
||||
ScanResult res;
|
||||
if (!zxRes.isValid()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
#if ZXING_VERSION < QT_VERSION_CHECK(1, 4, 0)
|
||||
// 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 < 0x20 && c != 0x0a && c != 0x0d;
|
||||
});
|
||||
if (hasWideChars || !hasControlChars) {
|
||||
res.d->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.d->content = b;
|
||||
}
|
||||
#else
|
||||
if (zxRes.contentType() == ZXing::ContentType::Text) {
|
||||
res.d->content = QString::fromStdString(zxRes.text());
|
||||
} else {
|
||||
QByteArray b;
|
||||
b.resize(zxRes.bytes().size());
|
||||
std::copy(zxRes.bytes().begin(), zxRes.bytes().end(), b.begin());
|
||||
res.d->content = b;
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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.d->boundingRect = QRect(QPoint(x1, y1), QPoint(x2, y2));
|
||||
|
||||
// apply frame transformations to the bounding rect
|
||||
res.d->boundingRect = transform.mapRect(res.d->boundingRect);
|
||||
res.d->format = Format::toFormat(zxRes.format());
|
||||
return res;
|
||||
}
|
||||
|
||||
#include "moc_scanresult.cpp"
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PRISON_SCANRESULT_H
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef PRISON_SCANRESULT_P_H
|
||||
#define PRISON_SCANRESULT_P_H
|
||||
|
||||
#include "scanresult.h"
|
||||
|
||||
#include <QSharedData>
|
||||
#include <QTransform>
|
||||
|
||||
#define ZX_USE_UTF8 1
|
||||
#include <ZXing/Result.h>
|
||||
|
||||
namespace Prison
|
||||
{
|
||||
|
||||
class ScanResultPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static ScanResult fromZXingResult(const ZXing::Result &zxRes, const QTransform &transform = QTransform());
|
||||
|
||||
QVariant content;
|
||||
QRect boundingRect;
|
||||
Format::BarcodeFormat format = Format::NoFormat;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // PRISON_SCANRESULT_P_H
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@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);
|
||||
|
||||
QVideoSink *m_sink = nullptr;
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
VideoScanner::VideoScanner(QObject *parent)
|
||||
: QObject(parent)
|
||||
, 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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#include "moc_videoscanner.cpp"
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef PRISON_VIDEOSCANNER_H
|
||||
#define PRISON_VIDEOSCANNER_H
|
||||
|
||||
#include "prisonscanner_export.h"
|
||||
#include "scanresult.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QVideoSink>
|
||||
|
||||
#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 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Prison::ScanResult result READ result NOTIFY resultChanged)
|
||||
Q_PROPERTY(Prison::Format::BarcodeFormats formats READ formats WRITE setFormats NOTIFY formatsChanged)
|
||||
|
||||
Q_PROPERTY(QVideoSink *videoSink READ videoSink WRITE setVideoSink NOTIFY videoSinkChanged)
|
||||
|
||||
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);
|
||||
|
||||
/** The video sink being scanned for barcodes. */
|
||||
QVideoSink *videoSink() const;
|
||||
/** Sets the video sink to scan for barcodes. */
|
||||
void setVideoSink(QVideoSink *sink);
|
||||
|
||||
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
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "videoscannerframe_p.h"
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
int VideoScannerFrame::bytesPerLine() const
|
||||
{
|
||||
return m_frame.bytesPerLine(0);
|
||||
}
|
||||
|
||||
QVideoFrameFormat::PixelFormat VideoScannerFrame::pixelFormat() const
|
||||
{
|
||||
return m_frame.pixelFormat();
|
||||
}
|
||||
|
||||
void VideoScannerFrame::map()
|
||||
{
|
||||
if (!m_frameData && m_image.isNull()) {
|
||||
m_frame.map(QVideoFrame::ReadOnly);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
return m_frame.bits(0);
|
||||
}
|
||||
|
||||
bool VideoScannerFrame::copyRequired() const
|
||||
{
|
||||
return m_frame.handleType() == QVideoFrame::RhiTextureHandle;
|
||||
}
|
||||
|
||||
void VideoScannerFrame::copyFrameData(QByteArray &buffer)
|
||||
{
|
||||
Q_ASSERT(m_frame.isMapped());
|
||||
|
||||
const auto size = frameDataSize();
|
||||
if (buffer.size() != size) {
|
||||
buffer.resize(size);
|
||||
}
|
||||
std::memcpy(buffer.data(), m_frame.bits(0), size);
|
||||
m_frameData = reinterpret_cast<const uint8_t *>(buffer.constData());
|
||||
}
|
||||
|
||||
int VideoScannerFrame::frameDataSize() const
|
||||
{
|
||||
Q_ASSERT(m_frame.isMapped());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoScannerFrame::needsConversion() const
|
||||
{
|
||||
switch (m_frame.pixelFormat()) {
|
||||
case QVideoFrameFormat::Format_Jpeg:
|
||||
case QVideoFrameFormat::Format_SamplerExternalOES:
|
||||
case QVideoFrameFormat::Format_SamplerRect:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoScannerFrame::convertToImage()
|
||||
{
|
||||
if (!m_image.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_frame.isMapped());
|
||||
m_image = m_frame.toImage();
|
||||
}
|
||||
|
||||
QImage VideoScannerFrame::image() const
|
||||
{
|
||||
return m_image;
|
||||
}
|
||||
|
||||
bool VideoScannerFrame::isVerticallyFlipped() const
|
||||
{
|
||||
return m_verticallyFlipped;
|
||||
}
|
||||
|
||||
Format::BarcodeFormats VideoScannerFrame::formats() const
|
||||
{
|
||||
return m_formats;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@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;
|
||||
int bytesPerLine() const;
|
||||
QVideoFrameFormat::PixelFormat pixelFormat() const;
|
||||
|
||||
/** 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();
|
||||
|
||||
/** Returns the frame as QImage.
|
||||
* Result is only valid after a call to convertToImage().
|
||||
*/
|
||||
QImage image() const;
|
||||
|
||||
/** 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
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "config-prison-scanner.h"
|
||||
#include "format_p.h"
|
||||
#include "imagescanner_p.h"
|
||||
#include "scanresult.h"
|
||||
#include "scanresult_p.h"
|
||||
#include "videoscannerframe_p.h"
|
||||
#include "videoscannerworker_p.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QTransform>
|
||||
|
||||
#define ZX_USE_UTF8 1
|
||||
#include <ZXing/ReadBarcode.h>
|
||||
|
||||
using namespace Prison;
|
||||
|
||||
VideoScannerWorker::VideoScannerWorker(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
connect(this, &VideoScannerWorker::scanFrameRequest, this, &VideoScannerWorker::slotScanFrame, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void VideoScannerWorker::slotScanFrame(VideoScannerFrame frame)
|
||||
{
|
||||
#if ZXING_VERSION < QT_VERSION_CHECK(1, 4, 0)
|
||||
ZXing::Result zxRes(ZXing::DecodeStatus::FormatError);
|
||||
#else
|
||||
ZXing::Result zxRes;
|
||||
#endif
|
||||
ZXing::DecodeHints hints;
|
||||
hints.setFormats(frame.formats() == Format::NoFormat ? ZXing::BarcodeFormats::all() : Format::toZXing(frame.formats()));
|
||||
|
||||
frame.map();
|
||||
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, frame.bytesPerLine()}, 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, frame.bytesPerLine()}, hints);
|
||||
break;
|
||||
case QVideoFrameFormat::Format_ABGR8888:
|
||||
case QVideoFrameFormat::Format_XBGR8888:
|
||||
zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::XBGR, frame.bytesPerLine()}, hints);
|
||||
break;
|
||||
case QVideoFrameFormat::Format_RGBA8888:
|
||||
case QVideoFrameFormat::Format_RGBX8888:
|
||||
zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::RGBX, frame.bytesPerLine()}, hints);
|
||||
break;
|
||||
case QVideoFrameFormat::Format_AYUV:
|
||||
case QVideoFrameFormat::Format_AYUV_Premultiplied:
|
||||
zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 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, frame.bytesPerLine()}, hints);
|
||||
break;
|
||||
case QVideoFrameFormat::Format_UYVY:
|
||||
zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 2}, hints);
|
||||
break;
|
||||
case QVideoFrameFormat::Format_YUYV:
|
||||
zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 2}, hints);
|
||||
break;
|
||||
case QVideoFrameFormat::Format_Y8:
|
||||
zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine()}, hints);
|
||||
break;
|
||||
case QVideoFrameFormat::Format_Y16:
|
||||
zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 1}, hints);
|
||||
break;
|
||||
case QVideoFrameFormat::Format_P010:
|
||||
case QVideoFrameFormat::Format_P016:
|
||||
case QVideoFrameFormat::Format_YUV420P10:
|
||||
zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 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 = ImageScanner::readBarcode(frame.image(), frame.formats());
|
||||
break;
|
||||
}
|
||||
frame.unmap();
|
||||
|
||||
// process scan result
|
||||
if (zxRes.isValid()) {
|
||||
QTransform t;
|
||||
if (frame.isVerticallyFlipped()) {
|
||||
t.scale(1, -1);
|
||||
t.translate(0, -frame.height());
|
||||
}
|
||||
Q_EMIT result(ScanResultPrivate::fromZXingResult(zxRes, t));
|
||||
} else {
|
||||
Q_EMIT result(ScanResult());
|
||||
}
|
||||
}
|
||||
|
||||
void VideoScannerThread::run()
|
||||
{
|
||||
exec();
|
||||
}
|
||||
|
||||
#include "moc_videoscannerworker_p.cpp"
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@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
|
||||
Reference in New Issue
Block a user