add_subdirectory(src/third-party/polyseed EXCLUDE_FROM_ALL)
endif()
-# ZBAR
+# ZXing
if(WITH_SCANNER)
- find_package(ZBAR REQUIRED)
- message(STATUS "libzbar: include dir at ${ZBAR_INCLUDE_DIR}")
- message(STATUS "libzbar: libraries at ${ZBAR_LIBRARIES}")
+ find_package(ZXing REQUIRED)
endif()
# libzip
+++ /dev/null
-find_package(PkgConfig)
-
-if(PkgConfig_FOUND)
- pkg_check_modules(PC_ZBAR QUIET zbar)
- if(PC_ZBAR_FOUND)
- set(ZBAR_DEFINITIONS ${PC_ZBAR_CFLAGS_OTHER})
- find_library(ZBAR_LIBRARIES NAMES zbar HINTS ${PC_ZBAR_LIBDIR} ${PC_ZBAR_LIBRARY_DIRS})
- find_path(ZBAR_INCLUDE_DIR Decoder.h HINTS ${PC_ZBAR_INCLUDEDIR} ${PC_ZBAR_INCLUDE_DIRS})
- endif()
-endif()
-
-if(NOT ZBAR_INCLUDE_DIR)
- find_path(ZBAR_H_PATH zbar.h)
- if(ZBAR_H_PATH)
- set(ZBAR_INCLUDE_DIR "${ZBAR_H_PATH}")
- endif()
-endif()
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(ZBAR DEFAULT_MSG ZBAR_LIBRARIES ZBAR_INCLUDE_DIR)
-message(STATUS "Found zbar libraries ${ZBAR_LIBRARIES}")
--- /dev/null
+############################################################################
+# FindZxing.txt
+# Copyright (C) 2018 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+############################################################################
+#
+# - Find the zxing include file and library
+#
+# ZXING_FOUND - system has zxing
+# ZXING_INCLUDE_DIRS - the zxing include directory
+# ZXING_LIBRARIES - The libraries needed to use zxing
+
+find_path(ZXING_INCLUDE_DIRS
+ NAMES
+ ZXing/BarcodeFormat.h
+ ZXing/BitHacks.h
+ ZXing/ByteArray.h
+ ZXing/CharacterSet.h
+ ZXing/Flags.h
+ ZXing/GTIN.h
+ ZXing/TextUtfEncoding.h
+ ZXing/ZXAlgorithms.h
+ ZXing/ZXConfig.h
+ PATH_SUFFIXES include
+)
+
+find_library(ZXING_LIBRARIES
+ NAMES ZXing libZXing
+ PATH_SUFFIXES Frameworks bin lib lib64
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(ZXing
+ DEFAULT_MSG
+ ZXING_INCLUDE_DIRS ZXING_LIBRARIES
+)
+
+mark_as_advanced(ZXING_INCLUDE_DIRS ZXING_LIBRARIES)
+
-packages := boost openssl libiconv unbound qrencode zbar sodium polyseed hidapi protobuf libusb zlib libgpg-error libgcrypt expat libzip bc-ur quirc
+packages := boost openssl libiconv unbound qrencode sodium polyseed hidapi protobuf libusb zlib libgpg-error libgcrypt expat libzip bc-ur zxing-cpp
native_packages := native_qt native_protobuf
linux_packages := eudev libfuse libsquashfuse zstd appimage_runtime
+++ /dev/null
-package=quirc
-$(package)_version=1.2
-$(package)_download_path=https://github.com/dlbeer/quirc/archive/refs/tags/
-$(package)_file_name=v1.2.tar.gz
-$(package)_sha256_hash=73c12ea33d337ec38fb81218c7674f57dba7ec0570bddd5c7f7a977c0deb64c5
-$(package)_patches += CMakeLists.txt
-
-define $(package)_preprocess_cmds
- cp $($(package)_patch_dir)/CMakeLists.txt CMakeLists.txt
-endef
-
-define $(package)_config_cmds
- $($(package)_cmake) -DCMAKE_INSTALL_PREFIX=$(host_prefix) .
-endef
-
-define $(package)_build_cmds
- $(MAKE)
-endef
-
-define $(package)_stage_cmds
- $(MAKE) DESTDIR=$($(package)_staging_dir) install
-endef
+++ /dev/null
-package=zbar
-$(package)_version=0.23.90
-$(package)_download_path=https://github.com/mchehab/zbar/archive/refs/tags/
-$(package)_download_file=$($(package)_version).tar.gz
-$(package)_file_name=$(package)-$($(package)_version).tar.gz
-$(package)_sha256_hash=25fdd6726d5c4c6f95c95d37591bfbb2dde63d13d0b10cb1350923ea8b11963b
-$(package)_dependencies=libiconv
-
-define $(package)_set_vars
- $(package)_cflags+=-fPIE
- $(package)_cxxflags+=-fPIE
-endef
-
-define $(package)_preprocess_cmds
- autoreconf -vfi
-endef
-
-define $(package)_set_vars
- $(package)_config_opts=--prefix=$(host_prefix) --disable-shared --without-imagemagick --disable-video --without-xv --with-gtk=no --with-python=no --enable-doc=no --host=$(host)
-endef
-
-define $(package)_config_cmds
- $($(package)_autoconf) $($(package)_config_opts)
-endef
-
-define $(package)_build_cmds
- $(MAKE) $($(package)_build_opts)
-endef
-
-define $(package)_stage_cmds
- $(MAKE) DESTDIR=$($(package)_staging_dir) install
-endef
\ No newline at end of file
--- /dev/null
+package=zxing-cpp
+$(package)_version=2.1.0
+$(package)_download_path=https://github.com/$(package)/$(package)/archive/refs/tags
+$(package)_download_file=v$($(package)_version).tar.gz
+$(package)_file_name=$(package)-$($(package)_version).tar.gz
+$(package)_sha256_hash=6d54e403592ec7a143791c6526c1baafddf4c0897bb49b1af72b70a0f0c4a3fe
+
+define $(package)_set_vars
+ $(package)_config_opts=-DBUILD_WRITERS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_SHARED_LIBS=OFF
+endef
+
+define $(package)_config_cmds
+ $($(package)_cmake) .
+endef
+
+define $(package)_build_cmds
+ $(MAKE)
+endef
+
+define $(package)_stage_cmds
+ $(MAKE) DESTDIR=$($(package)_staging_dir) install
+endef
+++ /dev/null
-cmake_minimum_required(VERSION 3.5)
-
-project(quirc)
-
-add_library(quirc STATIC
- lib/decode.c
- lib/identify.c
- lib/quirc.c
- lib/version_db.c
- )
-
-target_include_directories(quirc PUBLIC lib)
-
-install(TARGETS quirc)
-
-install(FILES
- lib/quirc.h
- DESTINATION include/quirc)
"qrcode/utils/*.cpp")
endif()
-if (WITH_SCANNER AND NOT Qt6_FOUND)
+if (WITH_SCANNER AND Qt6_FOUND)
file(GLOB SCANNER_FILES
"qrcode/scanner/*.h"
"qrcode/scanner/*.cpp")
endif()
-if (WITH_SCANNER AND Qt6_FOUND)
- file(GLOB SCANNER_FILES
- "qrcode/scanner_qt6/*.h"
- "qrcode/scanner_qt6/*.cpp")
-endif()
-
list(APPEND SOURCE_FILES
${UPDATER_FILES}
${QRCODE_UTILS_FILES}
if(WITH_SCANNER)
target_include_directories(feather PUBLIC
- ${ZBAR_INCLUDE_DIR}
${QtMultimedia_INCLUDE_DIRS}
${QtMultimediaWidgets_INCLUDE_DIRS}
+ ${ZXING_INCLUDE_DIRS}
)
endif()
target_compile_definitions(feather PRIVATE HAS_XMRIG=1)
endif()
-if(WITH_SCANNER)
+if(WITH_SCANNER AND Qt6_FOUND)
target_compile_definitions(feather PRIVATE WITH_SCANNER=1)
endif()
if (WITH_SCANNER)
target_link_libraries(feather
- ${ZBAR_LIBRARIES}
Qt::Multimedia
Qt::MultimediaWidgets
+ ${ZXING_LIBRARIES}
)
endif()
#include "Icons.h"
#include "libwalletqt/WalletManager.h"
-#if defined(WITH_SCANNER) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#if defined(WITH_SCANNER)
#include "qrcode/scanner/QrCodeScanDialog.h"
-#include <QtMultimedia/QCameraInfo>
-#elif defined(WITH_SCANNER)
-#include "qrcode/scanner_qt6//QrCodeScanDialog.h"
#include <QMediaDevices>
#endif
#include "QrCodeScanDialog.h"
#include "ui_QrCodeScanDialog.h"
+#include <QCamera>
+#include <QMediaDevices>
+#include <QCameraDevice>
#include <QMessageBox>
-#include <QtMultimedia/QCamera>
-#include <QtMultimedia/QCameraInfo>
+#include <QImageCapture>
+#include <QVideoFrame>
+
+#include "Utils.h"
QrCodeScanDialog::QrCodeScanDialog(QWidget *parent)
- : QDialog(parent)
- , ui(new Ui::QrCodeScanDialog)
+ : QDialog(parent)
+ , ui(new Ui::QrCodeScanDialog)
+ , m_sink(new QVideoSink(this))
{
ui->setupUi(this);
- this->setWindowTitle("Scan QR Code");
+ this->setWindowTitle("Scan QR code");
QPixmap pixmap = QPixmap(":/assets/images/warning.png");
ui->icon_warning->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation));
- m_cameras = QCameraInfo::availableCameras();
-
- for (const auto &camera : m_cameras) {
-#ifdef Q_OS_WIN
+ const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
+ for (const auto &camera : cameras) {
ui->combo_camera->addItem(camera.description());
-#else
- ui->combo_camera->addItem(camera.deviceName());
-#endif
}
-
+
connect(ui->combo_camera, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &QrCodeScanDialog::onCameraSwitched);
+ connect(ui->viewfinder->videoSink(), &QVideoSink::videoFrameChanged, this, &QrCodeScanDialog::handleFrameCaptured);
+
this->onCameraSwitched(0);
m_thread = new QrScanThread(this);
m_thread->start();
connect(m_thread, &QrScanThread::decoded, this, &QrCodeScanDialog::onDecoded);
- connect(m_thread, &QrScanThread::notifyError, this, &QrCodeScanDialog::notifyError);
+}
- connect(&m_imageTimer, &QTimer::timeout, this, &QrCodeScanDialog::takeImage);
- m_imageTimer.start(500);
+void QrCodeScanDialog::handleFrameCaptured(const QVideoFrame &frame) {
+ QImage img = this->videoFrameToImage(frame);
+ m_thread->addImage(img);
}
-void QrCodeScanDialog::onCameraSwitched(int index) {
- if (index >= m_cameras.size()) {
- return;
- }
+QImage QrCodeScanDialog::videoFrameToImage(const QVideoFrame &videoFrame)
+{
+ auto handleType = videoFrame.handleType();
- m_camera.reset(new QCamera(m_cameras.at(index)));
+ if (handleType == QVideoFrame::NoHandle) {
- auto captureMode = QCamera::CaptureStillImage;
- if (m_camera->isCaptureModeSupported(captureMode)) {
- m_camera->setCaptureMode(captureMode);
- }
+ QImage image = videoFrame.toImage();
- connect(m_camera.data(), QOverload<QCamera::Error>::of(&QCamera::error), this, &QrCodeScanDialog::displayCameraError);
- connect(m_camera.data(), &QCamera::statusChanged, [this](QCamera::Status status){
- bool unloaded = (status == QCamera::Status::UnloadedStatus);
- ui->frame_unavailable->setVisible(unloaded);
- });
+ if (image.isNull()) {
+ return {};
+ }
- m_imageCapture.reset(new QCameraImageCapture(m_camera.data()));
- if (!m_imageCapture->isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer)) {
- qDebug() << "Capture to buffer is NOT supported";
+ if (image.format() != QImage::Format_ARGB32) {
+ image = image.convertToFormat(QImage::Format_ARGB32);
+ }
+
+ return image.copy();
}
+
+ return {};
+}
- m_imageCapture->setCaptureDestination(QCameraImageCapture::CaptureToBuffer);
-
- connect(m_imageCapture.data(), &QCameraImageCapture::imageAvailable, this, &QrCodeScanDialog::processAvailableImage);
- connect(m_imageCapture.data(), QOverload<int, QCameraImageCapture::Error, const QString &>::of(&QCameraImageCapture::error),
- this, &QrCodeScanDialog::displayCaptureError);
- m_camera->setViewfinder(ui->viewfinder);
- m_camera->start();
-}
+void QrCodeScanDialog::onCameraSwitched(int index) {
+ const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
-void QrCodeScanDialog::displayCaptureError(int id, const QCameraImageCapture::Error error, const QString &errorString)
-{
- Q_UNUSED(id);
- Q_UNUSED(error);
- QMessageBox::warning(this, "Image Capture Error", errorString);
-}
+ if (index >= cameras.size()) {
+ return;
+ }
-void QrCodeScanDialog::displayCameraError()
-{
- QMessageBox::warning(this, "Camera Error", m_camera->errorString());
-}
+ m_camera.reset(new QCamera(cameras.at(index)));
+ m_captureSession.setCamera(m_camera.data());
+ m_captureSession.setVideoOutput(ui->viewfinder);
-void QrCodeScanDialog::processAvailableImage(int id, const QVideoFrame &frame) {
- Q_UNUSED(id);
- QImage img = frame.image();
- img.convertTo(QImage::Format_RGB32);
- m_thread->addImage(img);
-}
+ connect(m_camera.data(), &QCamera::activeChanged, [this](bool active){
+ ui->frame_unavailable->setVisible(!active);
+ });
-void QrCodeScanDialog::takeImage()
-{
- if (m_imageCapture->isReadyForCapture()) {
- m_imageCapture->capture();
- }
+ m_camera->start();
}
-void QrCodeScanDialog::onDecoded(int type, const QString &data) {
+void QrCodeScanDialog::onDecoded(const QString &data) {
decodedString = data;
this->accept();
}
-void QrCodeScanDialog::notifyError(const QString &msg) {
- qDebug() << "QrScanner error: " << msg;
-}
-
QrCodeScanDialog::~QrCodeScanDialog()
{
m_thread->stop();
#include <QDialog>
#include <QCamera>
-#include <QCameraImageCapture>
+#include <QScopedPointer>
+#include <QMediaCaptureSession>
#include <QTimer>
-#include <QVideoFrame>
+#include <QVideoSink>
#include "QrScanThread.h"
private slots:
void onCameraSwitched(int index);
- void onDecoded(int type, const QString &data);
- void notifyError(const QString &msg);
+ void onDecoded(const QString &data);
private:
- void processAvailableImage(int id, const QVideoFrame &frame);
- void displayCaptureError(int, QCameraImageCapture::Error, const QString &errorString);
- void displayCameraError();
- void takeImage();
+ QImage videoFrameToImage(const QVideoFrame &videoFrame);
+ void handleFrameCaptured(const QVideoFrame &videoFrame);
QScopedPointer<Ui::QrCodeScanDialog> ui;
- QScopedPointer<QCamera> m_camera;
- QScopedPointer<QCameraImageCapture> m_imageCapture;
-
QrScanThread *m_thread;
- QTimer m_imageTimer;
- QList<QCameraInfo> m_cameras;
+ QScopedPointer<QCamera> m_camera;
+ QMediaCaptureSession m_captureSession;
+ QVideoSink m_sink;
};
-#endif //FEATHER_QRCODESCANDIALOG_H
\ No newline at end of file
+
+#endif //FEATHER_QRCODESCANDIALOG_H
<rect>
<x>0</x>
<y>0</y>
- <width>400</width>
- <height>300</height>
+ <width>490</width>
+ <height>422</height>
</rect>
</property>
<property name="windowTitle">
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QCameraViewfinder" name="viewfinder" native="true">
+ <widget class="QVideoWidget" name="viewfinder" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="icon_warning">
<property name="sizePolicy">
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>10</width>
+ <width>55</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
- <widget class="QLabel" name="label">
+ <widget class="QLabel" name="label_2">
<property name="text">
<string>Lost connection to camera. Please restart scan dialog.</string>
</property>
</widget>
<customwidgets>
<customwidget>
- <class>QCameraViewfinder</class>
+ <class>QVideoWidget</class>
<extends>QWidget</extends>
- <header>qcameraviewfinder.h</header>
+ <header>qvideowidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "QrScanThread.h"
-#include <QtGlobal>
#include <QDebug>
+#include <ZXing/ReadBarcode.h>
+
QrScanThread::QrScanThread(QObject *parent)
: QThread(parent)
, m_running(true)
{
- m_scanner.set_handler(*this);
}
-void QrScanThread::image_callback(zbar::Image &image)
+void QrScanThread::processQImage(const QImage &qimg)
{
- qDebug() << "image_callback : Found Code ! " ;
- for (zbar::Image::SymbolIterator sym = image.symbol_begin(); sym != image.symbol_end(); ++sym) {
- if (!sym->get_count()) {
- QString data = QString::fromStdString(sym->get_data());
- emit decoded(sym->get_type(), data);
- }
- }
-}
+ const auto hints = ZXing::DecodeHints()
+ .setFormats(ZXing::BarcodeFormat::QRCode | ZXing::BarcodeFormat::DataMatrix)
+ .setTryHarder(true)
+ .setBinarizer(ZXing::Binarizer::FixedThreshold);
-void QrScanThread::processZImage(zbar::Image &image)
-{
- m_scanner.recycle_image(image);
- zbar::Image tmp = image.convert(zbar_fourcc('Y', '8', '0', '0'));
- m_scanner.scan(tmp);
- image.set_symbols(tmp.get_symbols());
-}
+ const auto result = QrCodeUtils::ReadBarcode(qimg, hints);
-bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst)
-{
- switch (qimg.format()) {
- case QImage::Format_RGB32 :
- case QImage::Format_ARGB32 :
- case QImage::Format_ARGB32_Premultiplied :
- break;
- default :
- qDebug() << "Format: " << qimg.format();
- emit notifyError(QString("Invalid QImage Format !"));
- return false;
- }
- unsigned int bpl( qimg.bytesPerLine() ), width( bpl / 4), height( qimg.height());
- dst.set_size(width, height);
- dst.set_format("BGR4");
- unsigned long datalen = qimg.sizeInBytes();
- dst.set_data(qimg.bits(), datalen);
- if((width * 4 != bpl) || (width * height * 4 > datalen)){
- emit notifyError(QString("QImage to Zbar::Image failed !"));
- return false;
- }
- return true;
-}
-
-void QrScanThread::processQImage(const QImage &qimg)
-{
- try {
- m_image = QSharedPointer<zbar::Image>(new zbar::Image());
- if (!zimageFromQImage(qimg, *m_image))
- return;
- processZImage(*m_image);
- }
- catch(std::exception &e) {
- qDebug() << "ERROR: " << e.what();
- emit notifyError(e.what());
+ if (result.isValid()) {
+ emit decoded(result.text());
}
}
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
-#ifndef _QRSCANTHREAD_H_
-#define _QRSCANTHREAD_H_
+#ifndef QRSCANTHREAD_H_
+#define QRSCANTHREAD_H_
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QEvent>
#include <QCamera>
-#include <zbar.h>
-class QrScanThread : public QThread, public zbar::Image::Handler
+#include <ZXing/ReadBarcode.h>
+
+#include "qrcode/utils/QrCodeUtils.h"
+
+class QrScanThread : public QThread
{
Q_OBJECT
public:
- QrScanThread(QObject *parent = nullptr);
+ explicit QrScanThread(QObject *parent = nullptr);
void addImage(const QImage &img);
virtual void stop();
signals:
- void decoded(int type, const QString &data);
- void notifyError(const QString &error, bool warning = false);
+ void decoded(const QString &data);
protected:
- virtual void run();
+ void run() override;
void processQImage(const QImage &);
- void processZImage(zbar::Image &image);
- virtual void image_callback(zbar::Image &image);
- bool zimageFromQImage(const QImage&, zbar::Image &);
private:
- zbar::ImageScanner m_scanner;
- QSharedPointer<zbar::Image> m_image;
bool m_running;
QMutex m_mutex;
QWaitCondition m_waitCondition;
+++ /dev/null
-// SPDX-License-Identifier: BSD-3-Clause
-// SPDX-FileCopyrightText: 2020-2023 The Monero Project
-
-#include "QrCodeScanDialog.h"
-#include "ui_QrCodeScanDialog.h"
-
-#include <QCamera>
-#include <QMediaDevices>
-#include <QCameraDevice>
-#include <QMessageBox>
-#include <QImageCapture>
-#include <QVideoFrame>
-
-#include "Utils.h"
-
-QrCodeScanDialog::QrCodeScanDialog(QWidget *parent)
- : QDialog(parent)
- , ui(new Ui::QrCodeScanDialog)
-{
- ui->setupUi(this);
- this->setWindowTitle("Scan QR code");
-
- QPixmap pixmap = QPixmap(":/assets/images/warning.png");
- ui->icon_warning->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation));
-
- const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
- for (const auto &camera : cameras) {
- ui->combo_camera->addItem(camera.description());
- }
-
- connect(ui->combo_camera, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &QrCodeScanDialog::onCameraSwitched);
-
- this->onCameraSwitched(0);
-
- m_thread = new QrScanThread(this);
- m_thread->start();
-
- connect(m_thread, &QrScanThread::decoded, this, &QrCodeScanDialog::onDecoded);
- connect(m_thread, &QrScanThread::notifyError, this, &QrCodeScanDialog::notifyError);
-
- connect(&m_imageTimer, &QTimer::timeout, this, &QrCodeScanDialog::takeImage);
- m_imageTimer.start(500);
-}
-
-void QrCodeScanDialog::onCameraSwitched(int index) {
- const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
-
- if (index >= cameras.size()) {
- return;
- }
-
- m_camera.reset(new QCamera(cameras.at(index)));
- m_captureSession.setCamera(m_camera.data());
- m_captureSession.setVideoOutput(ui->viewfinder);
-
- m_imageCapture = new QImageCapture;
- m_captureSession.setImageCapture(m_imageCapture);
-
- connect(m_imageCapture, &QImageCapture::imageCaptured, this, &QrCodeScanDialog::processCapturedImage);
- connect(m_camera.data(), &QCamera::errorOccurred, this, &QrCodeScanDialog::displayCameraError);
- connect(m_camera.data(), &QCamera::activeChanged, [this](bool active){
- ui->frame_unavailable->setVisible(!active);
- });
-
- m_camera->start();
-}
-
-void QrCodeScanDialog::processCapturedImage(int requestId, const QImage& img) {
- Q_UNUSED(requestId);
- QImage image{img};
- image.convertTo(QImage::Format_RGB32);
- m_thread->addImage(image);
-}
-
-void QrCodeScanDialog::takeImage()
-{
- if (m_imageCapture->isReadyForCapture()) {
- m_imageCapture->capture();
- }
-}
-
-void QrCodeScanDialog::onDecoded(int type, const QString &data) {
- decodedString = data;
- this->accept();
-}
-
-void QrCodeScanDialog::displayCameraError()
-{
- if (m_camera->error() != QCamera::NoError) {
- Utils::showError(this, "Camera error", m_camera->errorString());
- }
-}
-
-void QrCodeScanDialog::notifyError(const QString &msg) {
- qDebug() << "QrScanner error: " << msg;
-}
-
-QrCodeScanDialog::~QrCodeScanDialog()
-{
- m_thread->stop();
- m_thread->quit();
- if (!m_thread->wait(5000))
- {
- m_thread->terminate();
- m_thread->wait();
- }
-}
\ No newline at end of file
+++ /dev/null
-// SPDX-License-Identifier: BSD-3-Clause
-// SPDX-FileCopyrightText: 2020-2023 The Monero Project
-
-#ifndef FEATHER_QRCODESCANDIALOG_H
-#define FEATHER_QRCODESCANDIALOG_H
-
-#include <QDialog>
-#include <QCamera>
-#include <QScopedPointer>
-#include <QMediaCaptureSession>
-#include <QTimer>
-
-#include "QrScanThread.h"
-
-namespace Ui {
- class QrCodeScanDialog;
-}
-
-class QrCodeScanDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit QrCodeScanDialog(QWidget *parent);
- ~QrCodeScanDialog() override;
-
- QString decodedString = "";
-
-private slots:
- void onCameraSwitched(int index);
- void onDecoded(int type, const QString &data);
- void notifyError(const QString &msg);
-
-private:
- void processCapturedImage(int requestId, const QImage& img);
- void displayCameraError();
- void takeImage();
-
- QScopedPointer<Ui::QrCodeScanDialog> ui;
-
- QrScanThread *m_thread;
- QImageCapture *m_imageCapture;
- QTimer m_imageTimer;
- QScopedPointer<QCamera> m_camera;
- QMediaCaptureSession m_captureSession;
-};
-
-
-#endif //FEATHER_QRCODESCANDIALOG_H
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>QrCodeScanDialog</class>
- <widget class="QDialog" name="QrCodeScanDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>490</width>
- <height>422</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Dialog</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QVideoWidget" name="viewfinder" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QFrame" name="frame_unavailable">
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="QLabel" name="icon_warning">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>icon</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Preferred</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>55</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Lost connection to camera. Please restart scan dialog.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QLabel" name="label_3">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Camera:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="combo_camera"/>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>QVideoWidget</class>
- <extends>QWidget</extends>
- <header>qvideowidget.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
+++ /dev/null
-// SPDX-License-Identifier: BSD-3-Clause
-// SPDX-FileCopyrightText: 2020-2023 The Monero Project
-
-#include "QrScanThread.h"
-#include <QtGlobal>
-#include <QDebug>
-
-QrScanThread::QrScanThread(QObject *parent)
- : QThread(parent)
- , m_running(true)
-{
- m_scanner.set_handler(*this);
-}
-
-void QrScanThread::image_callback(zbar::Image &image)
-{
- qDebug() << "image_callback : Found Code ! " ;
- for (zbar::Image::SymbolIterator sym = image.symbol_begin(); sym != image.symbol_end(); ++sym) {
- if (!sym->get_count()) {
- QString data = QString::fromStdString(sym->get_data());
- emit decoded(sym->get_type(), data);
- }
- }
-}
-
-void QrScanThread::processZImage(zbar::Image &image)
-{
- m_scanner.recycle_image(image);
- zbar::Image tmp = image.convert(zbar_fourcc('Y', '8', '0', '0'));
- m_scanner.scan(tmp);
- image.set_symbols(tmp.get_symbols());
-}
-
-bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst)
-{
- switch (qimg.format()) {
- case QImage::Format_RGB32 :
- case QImage::Format_ARGB32 :
- case QImage::Format_ARGB32_Premultiplied :
- break;
- default :
- qDebug() << "Format: " << qimg.format();
- emit notifyError(QString("Invalid QImage Format !"));
- return false;
- }
- unsigned int bpl( qimg.bytesPerLine() ), width( bpl / 4), height( qimg.height());
- dst.set_size(width, height);
- dst.set_format("BGR4");
- unsigned long datalen = qimg.sizeInBytes();
- dst.set_data(qimg.bits(), datalen);
- if((width * 4 != bpl) || (width * height * 4 > datalen)){
- emit notifyError(QString("QImage to Zbar::Image failed !"));
- return false;
- }
- return true;
-}
-
-void QrScanThread::processQImage(const QImage &qimg)
-{
- try {
- m_image = QSharedPointer<zbar::Image>(new zbar::Image());
- if (!zimageFromQImage(qimg, *m_image))
- return;
- processZImage(*m_image);
- }
- catch(std::exception &e) {
- qDebug() << "ERROR: " << e.what();
- emit notifyError(e.what());
- }
-}
-
-void QrScanThread::stop()
-{
- m_running = false;
- m_waitCondition.wakeOne();
-}
-
-void QrScanThread::addImage(const QImage &img)
-{
- QMutexLocker locker(&m_mutex);
- m_queue.append(img);
- m_waitCondition.wakeOne();
-}
-
-void QrScanThread::run()
-{
- while (m_running) {
- QMutexLocker locker(&m_mutex);
- while (m_queue.isEmpty() && m_running) {
- m_waitCondition.wait(&m_mutex);
- }
- if (!m_queue.isEmpty()) {
- processQImage(m_queue.takeFirst());
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-// SPDX-License-Identifier: BSD-3-Clause
-// SPDX-FileCopyrightText: 2020-2023 The Monero Project
-
-#ifndef _QRSCANTHREAD_H_
-#define _QRSCANTHREAD_H_
-
-#include <QThread>
-#include <QMutex>
-#include <QWaitCondition>
-#include <QEvent>
-#include <QCamera>
-#include <zbar.h>
-
-class QrScanThread : public QThread, public zbar::Image::Handler
-{
- Q_OBJECT
-
-public:
- QrScanThread(QObject *parent = nullptr);
- void addImage(const QImage &img);
- virtual void stop();
-
-signals:
- void decoded(int type, const QString &data);
- void notifyError(const QString &error, bool warning = false);
-
-protected:
- virtual void run();
- void processQImage(const QImage &);
- void processZImage(zbar::Image &image);
- virtual void image_callback(zbar::Image &image);
- bool zimageFromQImage(const QImage&, zbar::Image &);
-
-private:
- zbar::ImageScanner m_scanner;
- QSharedPointer<zbar::Image> m_image;
- bool m_running;
- QMutex m_mutex;
- QWaitCondition m_waitCondition;
- QList<QImage> m_queue;
-};
-#endif
\ No newline at end of file
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "QrCodeUtils.h"
-#include <QDebug>
-
-bool QrCodeUtils::zimageFromQImage(const QImage &qImg, zbar::Image &dst) {
- qDebug() << qImg.format();
- switch (qImg.format()) {
- case QImage::Format_RGB32 :
- case QImage::Format_ARGB32 :
- case QImage::Format_ARGB32_Premultiplied :
- break;
- default :
- return false;
- }
-
- unsigned int bpl(qImg.bytesPerLine());
- unsigned int width(bpl / 4);
- unsigned int height(qImg.height());
-
- dst.set_size(width, height);
- dst.set_format("BGR4");
- unsigned long datalen = qImg.sizeInBytes();
- dst.set_data(qImg.bits(), datalen);
- if ((width * 4 != bpl) || (width * height * 4 > datalen)) {
- return false;
- }
- return true;
+
+Result QrCodeUtils::ReadBarcode(const QImage& img, const ZXing::DecodeHints& hints)
+{
+ auto ImgFmtFromQImg = [](const QImage& img){
+ switch (img.format()) {
+ case QImage::Format_ARGB32:
+ case QImage::Format_RGB32:
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ return ZXing::ImageFormat::BGRX;
+
+#else
+ return ZXing::ImageFormat::XRGB;
+
+#endif
+ case QImage::Format_RGB888: return ZXing::ImageFormat::RGB;
+
+ case QImage::Format_RGBX8888:
+ case QImage::Format_RGBA8888: return ZXing::ImageFormat::RGBX;
+
+ case QImage::Format_Grayscale8: return ZXing::ImageFormat::Lum;
+
+ default: return ZXing::ImageFormat::None;
+ }
+ };
+
+ auto exec = [&](const QImage& img){
+ return Result(ZXing::ReadBarcode({ img.bits(), img.width(), img.height(), ImgFmtFromQImg(img) }, hints));
+ };
+
+ return ImgFmtFromQImg(img) == ZXing::ImageFormat::None ? exec(img.convertToFormat(QImage::Format_RGBX8888)) : exec(img);
}
+
QString QrCodeUtils::scanImage(const QImage &img) {
- zbar::ImageScanner scanner;
- scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);
-
- zbar::Image zImg;
- int r = zimageFromQImage(img, zImg);
- if (!r) {
- qWarning() << "Unable to convert QImage into zbar::Image";
- return "";
- }
-
- zbar::Image scanImg = zImg.convert(zbar_fourcc('Y', '8', '0', '0'));
- scanner.scan(scanImg);
-
- QString result;
- for (zbar::Image::SymbolIterator sym = scanImg.symbol_begin(); sym != scanImg.symbol_end(); ++sym) {
- if (!sym->get_count()) {
- result = QString::fromStdString(sym->get_data());
- }
- }
- return result;
+ const auto hints = ZXing::DecodeHints()
+ .setFormats(ZXing::BarcodeFormat::QRCode | ZXing::BarcodeFormat::DataMatrix)
+ .setTryHarder(true)
+ .setBinarizer(ZXing::Binarizer::FixedThreshold);
+
+ const auto result = ReadBarcode(img, hints);
+
+ return result.text();
}
\ No newline at end of file
#define FEATHER_QRCODEUTILS_H
#include <QImage>
-#include <zbar.h>
+
+#include <ZXing/ReadBarcode.h>
+
+class Result : private ZXing::Result
+{
+public:
+ explicit Result(ZXing::Result&& r) :
+ m_result(std::move(r)){ }
+
+ inline QString text() const { return QString::fromStdString(m_result.text()); }
+ bool isValid() const { return m_result.isValid(); }
+
+private:
+ ZXing::Result m_result;
+};
class QrCodeUtils {
public:
- static bool zimageFromQImage(const QImage &qImg, zbar::Image &dst);
static QString scanImage(const QImage &img);
+ static Result ReadBarcode(const QImage& img, const ZXing::DecodeHints& hints = { });
};
-
#endif //FEATHER_QRCODEUTILS_H