mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-22 11:39:25 +00:00
scanner: replace zbar with zxing
This commit is contained in:
parent
0ef54aa4c8
commit
8a634dae13
22 changed files with 231 additions and 722 deletions
|
@ -92,11 +92,9 @@ if(Polyseed_SUBMODULE)
|
||||||
add_subdirectory(src/third-party/polyseed EXCLUDE_FROM_ALL)
|
add_subdirectory(src/third-party/polyseed EXCLUDE_FROM_ALL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ZBAR
|
# ZXing
|
||||||
if(WITH_SCANNER)
|
if(WITH_SCANNER)
|
||||||
find_package(ZBAR REQUIRED)
|
find_package(ZXing REQUIRED)
|
||||||
message(STATUS "libzbar: include dir at ${ZBAR_INCLUDE_DIR}")
|
|
||||||
message(STATUS "libzbar: libraries at ${ZBAR_LIBRARIES}")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# libzip
|
# libzip
|
||||||
|
|
|
@ -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}")
|
|
55
cmake/FindZXing.cmake
Normal file
55
cmake/FindZXing.cmake
Normal file
|
@ -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)
|
||||||
|
|
|
@ -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
|
native_packages := native_qt native_protobuf
|
||||||
|
|
||||||
linux_packages := eudev libfuse libsquashfuse zstd appimage_runtime
|
linux_packages := eudev libfuse libsquashfuse zstd appimage_runtime
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
22
contrib/depends/packages/zxing-cpp.mk
Normal file
22
contrib/depends/packages/zxing-cpp.mk
Normal file
|
@ -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
|
|
@ -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)
|
|
|
@ -85,18 +85,12 @@ if (WITH_SCANNER)
|
||||||
"qrcode/utils/*.cpp")
|
"qrcode/utils/*.cpp")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WITH_SCANNER AND NOT Qt6_FOUND)
|
if (WITH_SCANNER AND Qt6_FOUND)
|
||||||
file(GLOB SCANNER_FILES
|
file(GLOB SCANNER_FILES
|
||||||
"qrcode/scanner/*.h"
|
"qrcode/scanner/*.h"
|
||||||
"qrcode/scanner/*.cpp")
|
"qrcode/scanner/*.cpp")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WITH_SCANNER AND Qt6_FOUND)
|
|
||||||
file(GLOB SCANNER_FILES
|
|
||||||
"qrcode/scanner_qt6/*.h"
|
|
||||||
"qrcode/scanner_qt6/*.cpp")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
list(APPEND SOURCE_FILES
|
list(APPEND SOURCE_FILES
|
||||||
${UPDATER_FILES}
|
${UPDATER_FILES}
|
||||||
${QRCODE_UTILS_FILES}
|
${QRCODE_UTILS_FILES}
|
||||||
|
@ -161,9 +155,9 @@ target_include_directories(feather PUBLIC
|
||||||
|
|
||||||
if(WITH_SCANNER)
|
if(WITH_SCANNER)
|
||||||
target_include_directories(feather PUBLIC
|
target_include_directories(feather PUBLIC
|
||||||
${ZBAR_INCLUDE_DIR}
|
|
||||||
${QtMultimedia_INCLUDE_DIRS}
|
${QtMultimedia_INCLUDE_DIRS}
|
||||||
${QtMultimediaWidgets_INCLUDE_DIRS}
|
${QtMultimediaWidgets_INCLUDE_DIRS}
|
||||||
|
${ZXING_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -191,7 +185,7 @@ if(XMRIG)
|
||||||
target_compile_definitions(feather PRIVATE HAS_XMRIG=1)
|
target_compile_definitions(feather PRIVATE HAS_XMRIG=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_SCANNER)
|
if(WITH_SCANNER AND Qt6_FOUND)
|
||||||
target_compile_definitions(feather PRIVATE WITH_SCANNER=1)
|
target_compile_definitions(feather PRIVATE WITH_SCANNER=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -281,9 +275,9 @@ endif()
|
||||||
|
|
||||||
if (WITH_SCANNER)
|
if (WITH_SCANNER)
|
||||||
target_link_libraries(feather
|
target_link_libraries(feather
|
||||||
${ZBAR_LIBRARIES}
|
|
||||||
Qt::Multimedia
|
Qt::Multimedia
|
||||||
Qt::MultimediaWidgets
|
Qt::MultimediaWidgets
|
||||||
|
${ZXING_LIBRARIES}
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,8 @@
|
||||||
#include "Icons.h"
|
#include "Icons.h"
|
||||||
#include "libwalletqt/WalletManager.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 "qrcode/scanner/QrCodeScanDialog.h"
|
||||||
#include <QtMultimedia/QCameraInfo>
|
|
||||||
#elif defined(WITH_SCANNER)
|
|
||||||
#include "qrcode/scanner_qt6//QrCodeScanDialog.h"
|
|
||||||
#include <QMediaDevices>
|
#include <QMediaDevices>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -4,112 +4,94 @@
|
||||||
#include "QrCodeScanDialog.h"
|
#include "QrCodeScanDialog.h"
|
||||||
#include "ui_QrCodeScanDialog.h"
|
#include "ui_QrCodeScanDialog.h"
|
||||||
|
|
||||||
|
#include <QCamera>
|
||||||
|
#include <QMediaDevices>
|
||||||
|
#include <QCameraDevice>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QtMultimedia/QCamera>
|
#include <QImageCapture>
|
||||||
#include <QtMultimedia/QCameraInfo>
|
#include <QVideoFrame>
|
||||||
|
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
QrCodeScanDialog::QrCodeScanDialog(QWidget *parent)
|
QrCodeScanDialog::QrCodeScanDialog(QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, ui(new Ui::QrCodeScanDialog)
|
, ui(new Ui::QrCodeScanDialog)
|
||||||
|
, m_sink(new QVideoSink(this))
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
this->setWindowTitle("Scan QR Code");
|
this->setWindowTitle("Scan QR code");
|
||||||
|
|
||||||
QPixmap pixmap = QPixmap(":/assets/images/warning.png");
|
QPixmap pixmap = QPixmap(":/assets/images/warning.png");
|
||||||
ui->icon_warning->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation));
|
ui->icon_warning->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation));
|
||||||
|
|
||||||
m_cameras = QCameraInfo::availableCameras();
|
const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
|
||||||
|
for (const auto &camera : cameras) {
|
||||||
for (const auto &camera : m_cameras) {
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
ui->combo_camera->addItem(camera.description());
|
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->combo_camera, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &QrCodeScanDialog::onCameraSwitched);
|
||||||
|
|
||||||
|
connect(ui->viewfinder->videoSink(), &QVideoSink::videoFrameChanged, this, &QrCodeScanDialog::handleFrameCaptured);
|
||||||
|
|
||||||
this->onCameraSwitched(0);
|
this->onCameraSwitched(0);
|
||||||
|
|
||||||
m_thread = new QrScanThread(this);
|
m_thread = new QrScanThread(this);
|
||||||
m_thread->start();
|
m_thread->start();
|
||||||
|
|
||||||
connect(m_thread, &QrScanThread::decoded, this, &QrCodeScanDialog::onDecoded);
|
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) {
|
void QrCodeScanDialog::handleFrameCaptured(const QVideoFrame &frame) {
|
||||||
if (index >= m_cameras.size()) {
|
QImage img = this->videoFrameToImage(frame);
|
||||||
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<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);
|
|
||||||
});
|
|
||||||
|
|
||||||
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<int, QCameraImageCapture::Error, const QString &>::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);
|
|
||||||
m_thread->addImage(img);
|
m_thread->addImage(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QrCodeScanDialog::takeImage()
|
QImage QrCodeScanDialog::videoFrameToImage(const QVideoFrame &videoFrame)
|
||||||
{
|
{
|
||||||
if (m_imageCapture->isReadyForCapture()) {
|
auto handleType = videoFrame.handleType();
|
||||||
m_imageCapture->capture();
|
|
||||||
|
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<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);
|
||||||
|
|
||||||
|
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;
|
decodedString = data;
|
||||||
this->accept();
|
this->accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QrCodeScanDialog::notifyError(const QString &msg) {
|
|
||||||
qDebug() << "QrScanner error: " << msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
QrCodeScanDialog::~QrCodeScanDialog()
|
QrCodeScanDialog::~QrCodeScanDialog()
|
||||||
{
|
{
|
||||||
m_thread->stop();
|
m_thread->stop();
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QCamera>
|
#include <QCamera>
|
||||||
#include <QCameraImageCapture>
|
#include <QScopedPointer>
|
||||||
|
#include <QMediaCaptureSession>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QVideoFrame>
|
#include <QVideoSink>
|
||||||
|
|
||||||
#include "QrScanThread.h"
|
#include "QrScanThread.h"
|
||||||
|
|
||||||
|
@ -28,23 +29,19 @@ public:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onCameraSwitched(int index);
|
void onCameraSwitched(int index);
|
||||||
void onDecoded(int type, const QString &data);
|
void onDecoded(const QString &data);
|
||||||
void notifyError(const QString &msg);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void processAvailableImage(int id, const QVideoFrame &frame);
|
QImage videoFrameToImage(const QVideoFrame &videoFrame);
|
||||||
void displayCaptureError(int, QCameraImageCapture::Error, const QString &errorString);
|
void handleFrameCaptured(const QVideoFrame &videoFrame);
|
||||||
void displayCameraError();
|
|
||||||
void takeImage();
|
|
||||||
|
|
||||||
QScopedPointer<Ui::QrCodeScanDialog> ui;
|
QScopedPointer<Ui::QrCodeScanDialog> ui;
|
||||||
|
|
||||||
QScopedPointer<QCamera> m_camera;
|
|
||||||
QScopedPointer<QCameraImageCapture> m_imageCapture;
|
|
||||||
|
|
||||||
QrScanThread *m_thread;
|
QrScanThread *m_thread;
|
||||||
QTimer m_imageTimer;
|
QScopedPointer<QCamera> m_camera;
|
||||||
QList<QCameraInfo> m_cameras;
|
QMediaCaptureSession m_captureSession;
|
||||||
|
QVideoSink m_sink;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_QRCODESCANDIALOG_H
|
|
||||||
|
#endif //FEATHER_QRCODESCANDIALOG_H
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>490</width>
|
||||||
<height>300</height>
|
<height>422</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCameraViewfinder" name="viewfinder" native="true">
|
<widget class="QVideoWidget" name="viewfinder" native="true">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
<property name="frameShadow">
|
<property name="frameShadow">
|
||||||
<enum>QFrame::Raised</enum>
|
<enum>QFrame::Raised</enum>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="icon_warning">
|
<widget class="QLabel" name="icon_warning">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -56,14 +56,14 @@
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>10</width>
|
<width>55</width>
|
||||||
<height>0</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Lost connection to camera. Please restart scan dialog.</string>
|
<string>Lost connection to camera. Please restart scan dialog.</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -99,9 +99,9 @@
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>QCameraViewfinder</class>
|
<class>QVideoWidget</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>qcameraviewfinder.h</header>
|
<header>qvideowidget.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
|
|
|
@ -2,70 +2,27 @@
|
||||||
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
||||||
|
|
||||||
#include "QrScanThread.h"
|
#include "QrScanThread.h"
|
||||||
#include <QtGlobal>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <ZXing/ReadBarcode.h>
|
||||||
|
|
||||||
QrScanThread::QrScanThread(QObject *parent)
|
QrScanThread::QrScanThread(QObject *parent)
|
||||||
: QThread(parent)
|
: QThread(parent)
|
||||||
, m_running(true)
|
, 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)
|
void QrScanThread::processQImage(const QImage &qimg)
|
||||||
{
|
{
|
||||||
try {
|
const auto hints = ZXing::DecodeHints()
|
||||||
m_image = QSharedPointer<zbar::Image>(new zbar::Image());
|
.setFormats(ZXing::BarcodeFormat::QRCode | ZXing::BarcodeFormat::DataMatrix)
|
||||||
if (!zimageFromQImage(qimg, *m_image))
|
.setTryHarder(true)
|
||||||
return;
|
.setBinarizer(ZXing::Binarizer::FixedThreshold);
|
||||||
processZImage(*m_image);
|
|
||||||
}
|
const auto result = QrCodeUtils::ReadBarcode(qimg, hints);
|
||||||
catch(std::exception &e) {
|
|
||||||
qDebug() << "ERROR: " << e.what();
|
if (result.isValid()) {
|
||||||
emit notifyError(e.what());
|
emit decoded(result.text());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,39 +1,36 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
||||||
|
|
||||||
#ifndef _QRSCANTHREAD_H_
|
#ifndef QRSCANTHREAD_H_
|
||||||
#define _QRSCANTHREAD_H_
|
#define QRSCANTHREAD_H_
|
||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QWaitCondition>
|
#include <QWaitCondition>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <QCamera>
|
#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
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QrScanThread(QObject *parent = nullptr);
|
explicit QrScanThread(QObject *parent = nullptr);
|
||||||
void addImage(const QImage &img);
|
void addImage(const QImage &img);
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void decoded(int type, const QString &data);
|
void decoded(const QString &data);
|
||||||
void notifyError(const QString &error, bool warning = false);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void run();
|
void run() override;
|
||||||
void processQImage(const QImage &);
|
void processQImage(const QImage &);
|
||||||
void processZImage(zbar::Image &image);
|
|
||||||
virtual void image_callback(zbar::Image &image);
|
|
||||||
bool zimageFromQImage(const QImage&, zbar::Image &);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
zbar::ImageScanner m_scanner;
|
|
||||||
QSharedPointer<zbar::Image> m_image;
|
|
||||||
bool m_running;
|
bool m_running;
|
||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
QWaitCondition m_waitCondition;
|
QWaitCondition m_waitCondition;
|
||||||
|
|
|
@ -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 <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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 <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
|
|
|
@ -1,110 +0,0 @@
|
||||||
<?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>
|
|
|
@ -1,96 +0,0 @@
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
// 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
|
|
|
@ -2,52 +2,46 @@
|
||||||
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
||||||
|
|
||||||
#include "QrCodeUtils.h"
|
#include "QrCodeUtils.h"
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
bool QrCodeUtils::zimageFromQImage(const QImage &qImg, zbar::Image &dst) {
|
Result QrCodeUtils::ReadBarcode(const QImage& img, const ZXing::DecodeHints& hints)
|
||||||
qDebug() << qImg.format();
|
{
|
||||||
switch (qImg.format()) {
|
auto ImgFmtFromQImg = [](const QImage& img){
|
||||||
case QImage::Format_RGB32 :
|
switch (img.format()) {
|
||||||
case QImage::Format_ARGB32 :
|
case QImage::Format_ARGB32:
|
||||||
case QImage::Format_ARGB32_Premultiplied :
|
case QImage::Format_RGB32:
|
||||||
break;
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||||
default :
|
return ZXing::ImageFormat::BGRX;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int bpl(qImg.bytesPerLine());
|
#else
|
||||||
unsigned int width(bpl / 4);
|
return ZXing::ImageFormat::XRGB;
|
||||||
unsigned int height(qImg.height());
|
|
||||||
|
|
||||||
dst.set_size(width, height);
|
#endif
|
||||||
dst.set_format("BGR4");
|
case QImage::Format_RGB888: return ZXing::ImageFormat::RGB;
|
||||||
unsigned long datalen = qImg.sizeInBytes();
|
|
||||||
dst.set_data(qImg.bits(), datalen);
|
case QImage::Format_RGBX8888:
|
||||||
if ((width * 4 != bpl) || (width * height * 4 > datalen)) {
|
case QImage::Format_RGBA8888: return ZXing::ImageFormat::RGBX;
|
||||||
return false;
|
|
||||||
}
|
case QImage::Format_Grayscale8: return ZXing::ImageFormat::Lum;
|
||||||
return true;
|
|
||||||
|
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) {
|
QString QrCodeUtils::scanImage(const QImage &img) {
|
||||||
zbar::ImageScanner scanner;
|
const auto hints = ZXing::DecodeHints()
|
||||||
scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);
|
.setFormats(ZXing::BarcodeFormat::QRCode | ZXing::BarcodeFormat::DataMatrix)
|
||||||
|
.setTryHarder(true)
|
||||||
|
.setBinarizer(ZXing::Binarizer::FixedThreshold);
|
||||||
|
|
||||||
zbar::Image zImg;
|
const auto result = ReadBarcode(img, hints);
|
||||||
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'));
|
return result.text();
|
||||||
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;
|
|
||||||
}
|
}
|
|
@ -5,13 +5,26 @@
|
||||||
#define FEATHER_QRCODEUTILS_H
|
#define FEATHER_QRCODEUTILS_H
|
||||||
|
|
||||||
#include <QImage>
|
#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 {
|
class QrCodeUtils {
|
||||||
public:
|
public:
|
||||||
static bool zimageFromQImage(const QImage &qImg, zbar::Image &dst);
|
|
||||||
static QString scanImage(const QImage &img);
|
static QString scanImage(const QImage &img);
|
||||||
|
static Result ReadBarcode(const QImage& img, const ZXing::DecodeHints& hints = { });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif //FEATHER_QRCODEUTILS_H
|
#endif //FEATHER_QRCODEUTILS_H
|
||||||
|
|
Loading…
Reference in a new issue