QrScanner: macOS support

This commit is contained in:
tobtoht 2021-06-27 02:41:30 +02:00
parent 3db3e330d5
commit a1db9ade6b
No known key found for this signature in database
GPG key ID: 1CADD27F41F45C3C
8 changed files with 83 additions and 152 deletions

View file

@ -42,7 +42,7 @@ RUN git clone -b v3.18.4 --depth 1 https://github.com/Kitware/CMake && \
make -j$THREADS install && \ make -j$THREADS install && \
rm -rf $(pwd) rm -rf $(pwd)
# freetype2: Required for Qt 5.15 # freetype2: Required for Qt 5.15, fontconfig
RUN git clone -b VER-2-10-2 --depth 1 https://git.savannah.gnu.org/git/freetype/freetype2.git && \ RUN git clone -b VER-2-10-2 --depth 1 https://git.savannah.gnu.org/git/freetype/freetype2.git && \
cd freetype2 && \ cd freetype2 && \
git reset --hard 132f19b779828b194b3fede187cee719785db4d8 && \ git reset --hard 132f19b779828b194b3fede187cee719785db4d8 && \

View file

@ -59,6 +59,7 @@ depends:
mac-release: CMAKEFLAGS += -DSTATIC=Off mac-release: CMAKEFLAGS += -DSTATIC=Off
mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF) mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
mac-release: CMAKEFLAGS += -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, Off) mac-release: CMAKEFLAGS += -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, Off)
mac-release: CMAKEFLAGS += -DWITH_SCANNER=$(or ${WITH_SCANNER}, On)
mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64" mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64"
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
mac-release: mac-release:

View file

@ -11,7 +11,6 @@
QrCodeScanDialog::QrCodeScanDialog(QWidget *parent) QrCodeScanDialog::QrCodeScanDialog(QWidget *parent)
: QDialog(parent) : QDialog(parent)
, ui(new Ui::QrCodeScanDialog) , ui(new Ui::QrCodeScanDialog)
, m_scanner(new QrCodeScanner(this))
{ {
ui->setupUi(this); ui->setupUi(this);
this->setWindowTitle("Scan QR Code"); this->setWindowTitle("Scan QR Code");
@ -31,10 +30,16 @@ QrCodeScanDialog::QrCodeScanDialog(QWidget *parent)
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(m_scanner, &QrCodeScanner::decoded, this, &QrCodeScanDialog::onDecoded);
connect(m_scanner, &QrCodeScanner::notifyError, this, &QrCodeScanDialog::notifyError);
this->onCameraSwitched(0); 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) { void QrCodeScanDialog::onCameraSwitched(int index) {
@ -42,24 +47,60 @@ void QrCodeScanDialog::onCameraSwitched(int index) {
return; return;
} }
m_scanner->setSource(nullptr); m_camera.reset(new QCamera(m_cameras.at(index)));
delete m_camera;
m_camera = new QCamera(m_cameras.at(index), this); auto captureMode = QCamera::CaptureStillImage;
connect(m_camera, &QCamera::statusChanged, [this](QCamera::Status status){ 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); bool unloaded = (status == QCamera::Status::UnloadedStatus);
ui->frame_unavailable->setVisible(unloaded); ui->frame_unavailable->setVisible(unloaded);
}); });
m_camera->setCaptureMode(QCamera::CaptureViewfinder); 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->setViewfinder(ui->viewfinder);
m_scanner->setSource(m_camera);
m_scanner->setEnabled(true);
m_camera->start(); 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);
}
void QrCodeScanDialog::takeImage()
{
if (m_imageCapture->isReadyForCapture()) {
m_imageCapture->capture();
}
}
void QrCodeScanDialog::onDecoded(int type, const QString &data) { void QrCodeScanDialog::onDecoded(int type, const QString &data) {
decodedString = data; decodedString = data;
this->accept(); this->accept();
@ -71,6 +112,12 @@ void QrCodeScanDialog::notifyError(const QString &msg) {
QrCodeScanDialog::~QrCodeScanDialog() QrCodeScanDialog::~QrCodeScanDialog()
{ {
delete m_camera; m_thread->stop();
m_thread->quit();
if (!m_thread->wait(5000))
{
m_thread->terminate();
m_thread->wait();
}
delete ui; delete ui;
} }

View file

@ -6,8 +6,11 @@
#include <QDialog> #include <QDialog>
#include <QCamera> #include <QCamera>
#include <QCameraImageCapture>
#include <QTimer>
#include <QVideoFrame>
#include "QrCodeScanner.h" #include "QrScanThread.h"
namespace Ui { namespace Ui {
class QrCodeScanDialog; class QrCodeScanDialog;
@ -29,11 +32,19 @@ private slots:
void notifyError(const QString &msg); void notifyError(const QString &msg);
private: private:
void processAvailableImage(int id, const QVideoFrame &frame);
void displayCaptureError(int, QCameraImageCapture::Error, const QString &errorString);
void displayCameraError();
void takeImage();
Ui::QrCodeScanDialog *ui; Ui::QrCodeScanDialog *ui;
QScopedPointer<QCamera> m_camera;
QScopedPointer<QCameraImageCapture> m_imageCapture;
QrScanThread *m_thread;
QTimer m_imageTimer;
QList<QCameraInfo> m_cameras; QList<QCameraInfo> m_cameras;
QCamera *m_camera = nullptr;
QrCodeScanner *m_scanner;
}; };
#endif //FEATHER_QRCODESCANDIALOG_H #endif //FEATHER_QRCODESCANDIALOG_H

View file

@ -1,72 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2021, The Monero Project.
#include "QrCodeScanner.h"
#include <WalletManager.h>
#include <QVideoProbe>
#include <QCamera>
QrCodeScanner::QrCodeScanner(QObject *parent)
: QObject(parent)
, m_processTimerId(-1)
, m_processInterval(750)
, m_enabled(true)
{
m_probe = new QVideoProbe(this);
m_thread = new QrScanThread(this);
m_thread->start();
connect(m_thread, &QrScanThread::decoded, this, &QrCodeScanner::decoded);
connect(m_thread, &QrScanThread::notifyError, this, &QrCodeScanner::notifyError);
connect(m_probe, &QVideoProbe::videoFrameProbed, this, &QrCodeScanner::processFrame);
}
void QrCodeScanner::setSource(QCamera *camera)
{
m_probe->setSource((QMediaObject *)camera);
}
void QrCodeScanner::processFrame(const QVideoFrame &frame)
{
if (frame.isValid()){
m_curFrame = frame;
}
}
bool QrCodeScanner::enabled() const
{
return m_enabled;
}
void QrCodeScanner::setEnabled(bool enabled)
{
m_enabled = enabled;
if(!enabled && (m_processTimerId != -1) )
{
this->killTimer(m_processTimerId);
m_processTimerId = -1;
}
else if (enabled && (m_processTimerId == -1) )
{
m_processTimerId = this->startTimer(m_processInterval);
}
emit enabledChanged();
}
void QrCodeScanner::timerEvent(QTimerEvent *event)
{
if (event->timerId() == m_processTimerId) {
m_thread->addFrame(m_curFrame);
}
}
QrCodeScanner::~QrCodeScanner()
{
m_thread->stop();
m_thread->quit();
if (!m_thread->wait(5000))
{
m_thread->terminate();
m_thread->wait();
}
}

View file

@ -1,48 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2020, The Monero Project.
#ifndef QRCODESCANNER_H_
#define QRCODESCANNER_H_
#include <QImage>
#include <QVideoFrame>
#include "QrScanThread.h"
class QVideoProbe;
class QCamera;
class QrCodeScanner : public QObject
{
Q_OBJECT
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
public:
explicit QrCodeScanner(QObject *parent = nullptr);
~QrCodeScanner() override;
void setSource(QCamera*);
bool enabled() const;
void setEnabled(bool enabled);
public slots:
void processFrame(const QVideoFrame &frame);
signals:
void enabledChanged();
void decoded(int type, const QString &data);
void decode(int type, const QString &data);
void notifyError(const QString &error, bool warning = false);
protected:
void timerEvent(QTimerEvent *);
QrScanThread *m_thread;
int m_processTimerId;
int m_processInterval;
int m_enabled;
QVideoFrame m_curFrame;
QVideoProbe *m_probe;
};
#endif

View file

@ -33,7 +33,7 @@ void QrScanThread::processZImage(zbar::Image &image)
bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst) bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst)
{ {
switch( qimg.format() ){ switch (qimg.format()) {
case QImage::Format_RGB32 : case QImage::Format_RGB32 :
case QImage::Format_ARGB32 : case QImage::Format_ARGB32 :
case QImage::Format_ARGB32_Premultiplied : case QImage::Format_ARGB32_Premultiplied :
@ -69,34 +69,28 @@ void QrScanThread::processQImage(const QImage &qimg)
} }
} }
void QrScanThread::processVideoFrame(const QVideoFrame &frame)
{
processQImage(frame.image());
}
void QrScanThread::stop() void QrScanThread::stop()
{ {
m_running = false; m_running = false;
m_waitCondition.wakeOne(); m_waitCondition.wakeOne();
} }
void QrScanThread::addFrame(const QVideoFrame &frame) void QrScanThread::addImage(const QImage &img)
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
m_queue.append(frame); m_queue.append(img);
m_waitCondition.wakeOne(); m_waitCondition.wakeOne();
} }
void QrScanThread::run() void QrScanThread::run()
{ {
QVideoFrame frame;
while (m_running) { while (m_running) {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
while (m_queue.isEmpty() && m_running) { while (m_queue.isEmpty() && m_running) {
m_waitCondition.wait(&m_mutex); m_waitCondition.wait(&m_mutex);
} }
if (!m_queue.isEmpty()) { if (!m_queue.isEmpty()) {
processVideoFrame(m_queue.takeFirst()); processQImage(m_queue.takeFirst());
} }
} }
} }

View file

@ -8,7 +8,6 @@
#include <QMutex> #include <QMutex>
#include <QWaitCondition> #include <QWaitCondition>
#include <QEvent> #include <QEvent>
#include <QVideoFrame>
#include <QCamera> #include <QCamera>
#include <zbar.h> #include <zbar.h>
@ -18,7 +17,7 @@ class QrScanThread : public QThread, public zbar::Image::Handler
public: public:
QrScanThread(QObject *parent = nullptr); QrScanThread(QObject *parent = nullptr);
void addFrame(const QVideoFrame &frame); void addImage(const QImage &img);
virtual void stop(); virtual void stop();
signals: signals:
@ -27,7 +26,6 @@ signals:
protected: protected:
virtual void run(); virtual void run();
void processVideoFrame(const QVideoFrame &);
void processQImage(const QImage &); void processQImage(const QImage &);
void processZImage(zbar::Image &image); void processZImage(zbar::Image &image);
virtual void image_callback(zbar::Image &image); virtual void image_callback(zbar::Image &image);
@ -39,6 +37,6 @@ private:
bool m_running; bool m_running;
QMutex m_mutex; QMutex m_mutex;
QWaitCondition m_waitCondition; QWaitCondition m_waitCondition;
QList<QVideoFrame> m_queue; QList<QImage> m_queue;
}; };
#endif #endif