diff --git a/CMakeLists.txt b/CMakeLists.txt index c84d470..79942fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,11 +92,9 @@ if(Polyseed_SUBMODULE) 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 diff --git a/cmake/FindZBAR.cmake b/cmake/FindZBAR.cmake deleted file mode 100644 index 290f333..0000000 --- a/cmake/FindZBAR.cmake +++ /dev/null @@ -1,21 +0,0 @@ -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}") diff --git a/cmake/FindZXing.cmake b/cmake/FindZXing.cmake new file mode 100644 index 0000000..ee03f13 --- /dev/null +++ b/cmake/FindZXing.cmake @@ -0,0 +1,55 @@ +############################################################################ +# 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) + diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index ab03407..263e082 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,4 +1,4 @@ -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 diff --git a/contrib/depends/packages/quirc.mk b/contrib/depends/packages/quirc.mk deleted file mode 100644 index 2c32dc6..0000000 --- a/contrib/depends/packages/quirc.mk +++ /dev/null @@ -1,22 +0,0 @@ -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 diff --git a/contrib/depends/packages/zbar.mk b/contrib/depends/packages/zbar.mk deleted file mode 100644 index e8d11f9..0000000 --- a/contrib/depends/packages/zbar.mk +++ /dev/null @@ -1,32 +0,0 @@ -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 diff --git a/contrib/depends/packages/zxing-cpp.mk b/contrib/depends/packages/zxing-cpp.mk new file mode 100644 index 0000000..260d2ce --- /dev/null +++ b/contrib/depends/packages/zxing-cpp.mk @@ -0,0 +1,22 @@ +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 diff --git a/contrib/depends/patches/quirc/CMakeLists.txt b/contrib/depends/patches/quirc/CMakeLists.txt deleted file mode 100644 index aef0598..0000000 --- a/contrib/depends/patches/quirc/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -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) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 17d73fa..fdad5df 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -85,18 +85,12 @@ if (WITH_SCANNER) "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} @@ -161,9 +155,9 @@ target_include_directories(feather PUBLIC if(WITH_SCANNER) target_include_directories(feather PUBLIC - ${ZBAR_INCLUDE_DIR} ${QtMultimedia_INCLUDE_DIRS} ${QtMultimediaWidgets_INCLUDE_DIRS} + ${ZXING_INCLUDE_DIRS} ) endif() @@ -191,7 +185,7 @@ if(XMRIG) 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() @@ -281,9 +275,9 @@ endif() if (WITH_SCANNER) target_link_libraries(feather - ${ZBAR_LIBRARIES} Qt::Multimedia Qt::MultimediaWidgets + ${ZXING_LIBRARIES} ) endif() diff --git a/src/SendWidget.cpp b/src/SendWidget.cpp index 43d2a6f..21d1fd6 100644 --- a/src/SendWidget.cpp +++ b/src/SendWidget.cpp @@ -13,11 +13,8 @@ #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 -#elif defined(WITH_SCANNER) -#include "qrcode/scanner_qt6//QrCodeScanDialog.h" #include #endif diff --git a/src/qrcode/scanner/QrCodeScanDialog.cpp b/src/qrcode/scanner/QrCodeScanDialog.cpp index 78388c1..6bc4eb1 100644 --- a/src/qrcode/scanner/QrCodeScanDialog.cpp +++ b/src/qrcode/scanner/QrCodeScanDialog.cpp @@ -4,112 +4,94 @@ #include "QrCodeScanDialog.h" #include "ui_QrCodeScanDialog.h" +#include +#include +#include #include -#include -#include +#include +#include + +#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 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::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::onCameraSwitched(int index) { - if (index >= m_cameras.size()) { - return; - } - - m_camera.reset(new QCamera(m_cameras.at(index))); - - auto captureMode = QCamera::CaptureStillImage; - if (m_camera->isCaptureModeSupported(captureMode)) { - m_camera->setCaptureMode(captureMode); - } - - connect(m_camera.data(), QOverload::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); - }); - - m_imageCapture.reset(new QCameraImageCapture(m_camera.data())); - if (!m_imageCapture->isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer)) { - qDebug() << "Capture to buffer is NOT supported"; - } - - m_imageCapture->setCaptureDestination(QCameraImageCapture::CaptureToBuffer); - - connect(m_imageCapture.data(), &QCameraImageCapture::imageAvailable, this, &QrCodeScanDialog::processAvailableImage); - connect(m_imageCapture.data(), QOverload::of(&QCameraImageCapture::error), - this, &QrCodeScanDialog::displayCaptureError); - - m_camera->setViewfinder(ui->viewfinder); - m_camera->start(); -} - -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); -} - -void QrCodeScanDialog::displayCameraError() -{ - QMessageBox::warning(this, "Camera Error", m_camera->errorString()); -} - -void QrCodeScanDialog::processAvailableImage(int id, const QVideoFrame &frame) { - Q_UNUSED(id); - QImage img = frame.image(); - img.convertTo(QImage::Format_RGB32); +void QrCodeScanDialog::handleFrameCaptured(const QVideoFrame &frame) { + QImage img = this->videoFrameToImage(frame); m_thread->addImage(img); } -void QrCodeScanDialog::takeImage() +QImage QrCodeScanDialog::videoFrameToImage(const QVideoFrame &videoFrame) { - if (m_imageCapture->isReadyForCapture()) { - m_imageCapture->capture(); + auto handleType = videoFrame.handleType(); + + if (handleType == QVideoFrame::NoHandle) { + + QImage image = videoFrame.toImage(); + + if (image.isNull()) { + return {}; + } + + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + + return image.copy(); } + + return {}; } -void QrCodeScanDialog::onDecoded(int type, const QString &data) { + +void QrCodeScanDialog::onCameraSwitched(int index) { + const QList 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); + + connect(m_camera.data(), &QCamera::activeChanged, [this](bool active){ + ui->frame_unavailable->setVisible(!active); + }); + + m_camera->start(); +} + +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(); diff --git a/src/qrcode/scanner/QrCodeScanDialog.h b/src/qrcode/scanner/QrCodeScanDialog.h index 47735d6..43ac9b0 100644 --- a/src/qrcode/scanner/QrCodeScanDialog.h +++ b/src/qrcode/scanner/QrCodeScanDialog.h @@ -6,9 +6,10 @@ #include #include -#include +#include +#include #include -#include +#include #include "QrScanThread.h" @@ -28,23 +29,19 @@ public: 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; - QScopedPointer m_camera; - QScopedPointer m_imageCapture; - QrScanThread *m_thread; - QTimer m_imageTimer; - QList m_cameras; + QScopedPointer m_camera; + QMediaCaptureSession m_captureSession; + QVideoSink m_sink; }; -#endif //FEATHER_QRCODESCANDIALOG_H \ No newline at end of file + +#endif //FEATHER_QRCODESCANDIALOG_H diff --git a/src/qrcode/scanner/QrCodeScanDialog.ui b/src/qrcode/scanner/QrCodeScanDialog.ui index fb555eb..9cda999 100644 --- a/src/qrcode/scanner/QrCodeScanDialog.ui +++ b/src/qrcode/scanner/QrCodeScanDialog.ui @@ -6,8 +6,8 @@ 0 0 - 400 - 300 + 490 + 422 @@ -15,7 +15,7 @@ - + 0 @@ -32,7 +32,7 @@ QFrame::Raised - + @@ -56,14 +56,14 @@ - 10 + 55 0 - + Lost connection to camera. Please restart scan dialog. @@ -99,9 +99,9 @@ - QCameraViewfinder + QVideoWidget QWidget -
qcameraviewfinder.h
+
qvideowidget.h
1
diff --git a/src/qrcode/scanner/QrScanThread.cpp b/src/qrcode/scanner/QrScanThread.cpp index 54a699b..98f1f79 100644 --- a/src/qrcode/scanner/QrScanThread.cpp +++ b/src/qrcode/scanner/QrScanThread.cpp @@ -2,70 +2,27 @@ // SPDX-FileCopyrightText: 2020-2023 The Monero Project #include "QrScanThread.h" -#include #include +#include + 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(new zbar::Image()); - if (!zimageFromQImage(qimg, *m_image)) - return; - processZImage(*m_image); - } - catch(std::exception &e) { - qDebug() << "ERROR: " << e.what(); - emit notifyError(e.what()); + const auto hints = ZXing::DecodeHints() + .setFormats(ZXing::BarcodeFormat::QRCode | ZXing::BarcodeFormat::DataMatrix) + .setTryHarder(true) + .setBinarizer(ZXing::Binarizer::FixedThreshold); + + const auto result = QrCodeUtils::ReadBarcode(qimg, hints); + + if (result.isValid()) { + emit decoded(result.text()); } } diff --git a/src/qrcode/scanner/QrScanThread.h b/src/qrcode/scanner/QrScanThread.h index 849e775..771f352 100644 --- a/src/qrcode/scanner/QrScanThread.h +++ b/src/qrcode/scanner/QrScanThread.h @@ -1,39 +1,36 @@ // 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 #include #include #include #include -#include -class QrScanThread : public QThread, public zbar::Image::Handler +#include + +#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 m_image; bool m_running; QMutex m_mutex; QWaitCondition m_waitCondition; diff --git a/src/qrcode/scanner_qt6/QrCodeScanDialog.cpp b/src/qrcode/scanner_qt6/QrCodeScanDialog.cpp deleted file mode 100644 index 21a2e77..0000000 --- a/src/qrcode/scanner_qt6/QrCodeScanDialog.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: 2020-2023 The Monero Project - -#include "QrCodeScanDialog.h" -#include "ui_QrCodeScanDialog.h" - -#include -#include -#include -#include -#include -#include - -#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 cameras = QMediaDevices::videoInputs(); - for (const auto &camera : cameras) { - ui->combo_camera->addItem(camera.description()); - } - - connect(ui->combo_camera, QOverload::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 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 diff --git a/src/qrcode/scanner_qt6/QrCodeScanDialog.h b/src/qrcode/scanner_qt6/QrCodeScanDialog.h deleted file mode 100644 index 74b82e3..0000000 --- a/src/qrcode/scanner_qt6/QrCodeScanDialog.h +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: 2020-2023 The Monero Project - -#ifndef FEATHER_QRCODESCANDIALOG_H -#define FEATHER_QRCODESCANDIALOG_H - -#include -#include -#include -#include -#include - -#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; - - QrScanThread *m_thread; - QImageCapture *m_imageCapture; - QTimer m_imageTimer; - QScopedPointer m_camera; - QMediaCaptureSession m_captureSession; -}; - - -#endif //FEATHER_QRCODESCANDIALOG_H diff --git a/src/qrcode/scanner_qt6/QrCodeScanDialog.ui b/src/qrcode/scanner_qt6/QrCodeScanDialog.ui deleted file mode 100644 index 9cda999..0000000 --- a/src/qrcode/scanner_qt6/QrCodeScanDialog.ui +++ /dev/null @@ -1,110 +0,0 @@ - - - QrCodeScanDialog - - - - 0 - 0 - 490 - 422 - - - - Dialog - - - - - - - 0 - 0 - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - 0 - 0 - - - - icon - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 55 - 0 - - - - - - - - Lost connection to camera. Please restart scan dialog. - - - true - - - - - - - - - - - - - 0 - 0 - - - - Camera: - - - - - - - - - - - - - QVideoWidget - QWidget -
qvideowidget.h
- 1 -
-
- - -
diff --git a/src/qrcode/scanner_qt6/QrScanThread.cpp b/src/qrcode/scanner_qt6/QrScanThread.cpp deleted file mode 100644 index 54a699b..0000000 --- a/src/qrcode/scanner_qt6/QrScanThread.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: 2020-2023 The Monero Project - -#include "QrScanThread.h" -#include -#include - -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(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 diff --git a/src/qrcode/scanner_qt6/QrScanThread.h b/src/qrcode/scanner_qt6/QrScanThread.h deleted file mode 100644 index 849e775..0000000 --- a/src/qrcode/scanner_qt6/QrScanThread.h +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: 2020-2023 The Monero Project - -#ifndef _QRSCANTHREAD_H_ -#define _QRSCANTHREAD_H_ - -#include -#include -#include -#include -#include -#include - -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 m_image; - bool m_running; - QMutex m_mutex; - QWaitCondition m_waitCondition; - QList m_queue; -}; -#endif \ No newline at end of file diff --git a/src/qrcode/utils/QrCodeUtils.cpp b/src/qrcode/utils/QrCodeUtils.cpp index beb8d9f..c531c7f 100644 --- a/src/qrcode/utils/QrCodeUtils.cpp +++ b/src/qrcode/utils/QrCodeUtils.cpp @@ -2,52 +2,46 @@ // SPDX-FileCopyrightText: 2020-2023 The Monero Project #include "QrCodeUtils.h" -#include -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; - } +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; - unsigned int bpl(qImg.bytesPerLine()); - unsigned int width(bpl / 4); - unsigned int height(qImg.height()); +#else + return ZXing::ImageFormat::XRGB; - 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; +#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); + const auto hints = ZXing::DecodeHints() + .setFormats(ZXing::BarcodeFormat::QRCode | ZXing::BarcodeFormat::DataMatrix) + .setTryHarder(true) + .setBinarizer(ZXing::Binarizer::FixedThreshold); - zbar::Image zImg; - int r = zimageFromQImage(img, zImg); - if (!r) { - qWarning() << "Unable to convert QImage into zbar::Image"; - return ""; - } + const auto result = ReadBarcode(img, hints); - 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; + return result.text(); } \ No newline at end of file diff --git a/src/qrcode/utils/QrCodeUtils.h b/src/qrcode/utils/QrCodeUtils.h index f934838..2898fe4 100644 --- a/src/qrcode/utils/QrCodeUtils.h +++ b/src/qrcode/utils/QrCodeUtils.h @@ -5,13 +5,26 @@ #define FEATHER_QRCODEUTILS_H #include -#include + +#include + +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