mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-22 03:29:24 +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)
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
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")
|
||||
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()
|
||||
|
||||
|
|
|
@ -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 <QtMultimedia/QCameraInfo>
|
||||
#elif defined(WITH_SCANNER)
|
||||
#include "qrcode/scanner_qt6//QrCodeScanDialog.h"
|
||||
#include <QMediaDevices>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -4,112 +4,94 @@
|
|||
#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::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<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);
|
||||
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<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;
|
||||
this->accept();
|
||||
}
|
||||
|
||||
void QrCodeScanDialog::notifyError(const QString &msg) {
|
||||
qDebug() << "QrScanner error: " << msg;
|
||||
}
|
||||
|
||||
QrCodeScanDialog::~QrCodeScanDialog()
|
||||
{
|
||||
m_thread->stop();
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
|
||||
#include <QDialog>
|
||||
#include <QCamera>
|
||||
#include <QCameraImageCapture>
|
||||
#include <QScopedPointer>
|
||||
#include <QMediaCaptureSession>
|
||||
#include <QTimer>
|
||||
#include <QVideoFrame>
|
||||
#include <QVideoSink>
|
||||
|
||||
#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::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
|
||||
|
||||
#endif //FEATHER_QRCODESCANDIALOG_H
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
<width>490</width>
|
||||
<height>422</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -15,7 +15,7 @@
|
|||
</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>
|
||||
|
@ -32,7 +32,7 @@
|
|||
<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">
|
||||
|
@ -56,14 +56,14 @@
|
|||
</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>
|
||||
|
@ -99,9 +99,9 @@
|
|||
</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>
|
||||
|
|
|
@ -2,70 +2,27 @@
|
|||
// 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)
|
||||
{
|
||||
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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <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;
|
||||
|
|
|
@ -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
|
||||
|
||||
#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;
|
||||
}
|
||||
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();
|
||||
}
|
|
@ -5,13 +5,26 @@
|
|||
#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
|
||||
|
|
Loading…
Reference in a new issue