From 93d2eb5e1a064eb0eb1ad376cca71f2a3519e5ec Mon Sep 17 00:00:00 2001 From: MoroccanMalinois Date: Tue, 31 Jan 2017 04:36:08 +0000 Subject: [PATCH] add qrcode scanner --- README.md | 6 +- build.sh | 4 +- components/QRCodeScanner.qml | 134 ++++++++++++++++++++++++++ main.cpp | 13 +++ main.qml | 7 +- monero-wallet-gui.pro | 26 ++++- pages/AddressBook.qml | 31 +++++- pages/Transfer.qml | 31 +++++- qml.qrc | 1 + src/QR-Code-scanner/QrCodeScanner.cpp | 108 +++++++++++++++++++++ src/QR-Code-scanner/QrCodeScanner.h | 79 +++++++++++++++ src/QR-Code-scanner/QrScanThread.cpp | 132 +++++++++++++++++++++++++ src/QR-Code-scanner/QrScanThread.h | 69 +++++++++++++ 13 files changed, 627 insertions(+), 14 deletions(-) create mode 100644 components/QRCodeScanner.qml create mode 100644 src/QR-Code-scanner/QrCodeScanner.cpp create mode 100644 src/QR-Code-scanner/QrCodeScanner.h create mode 100644 src/QR-Code-scanner/QrScanThread.cpp create mode 100644 src/QR-Code-scanner/QrScanThread.h diff --git a/README.md b/README.md index cd23954b..c34a882e 100644 --- a/README.md +++ b/README.md @@ -85,15 +85,15 @@ Packaging for your favorite distribution would be a welcome contribution! - For Ubuntu 16.04 i386 - `sudo apt-get install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-xmllistmodel qttools5-dev-tools qml-module-qtquick-dialogs` + `sudo apt-get install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-xmllistmodel qttools5-dev-tools qml-module-qtquick-dialogs libzbar-dev` - For Ubuntu 16.04 x64 - `sudo apt-get install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-xmllistmodel qttools5-dev-tools qml-module-qtquick-dialogs qml-module-qt-labs-settings libqt5qml-graphicaleffects` + `sudo apt-get install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-xmllistmodel qttools5-dev-tools qml-module-qtquick-dialogs qml-module-qt-labs-settings libqt5qml-graphicaleffects libzbar-dev` - For Linux Mint 18 "Sarah" - Cinnamon (64-bit) - `sudo apt install qml-module-qt-labs-settings qml-module-qtgraphicaleffects` + `sudo apt install qml-module-qt-labs-settings qml-module-qtgraphicaleffects libzbar-dev` 6. Build the GUI. diff --git a/build.sh b/build.sh index 07be08c8..a9e5ea91 100755 --- a/build.sh +++ b/build.sh @@ -24,12 +24,12 @@ elif [ "$BUILD_TYPE" == "release-static" ]; then BIN_PATH=release/bin elif [ "$BUILD_TYPE" == "release-android" ]; then echo "Building release for ANDROID" - CONFIG="CONFIG+=release static"; + CONFIG="CONFIG+=release static WITH_SCANNER"; ANDROID=true BIN_PATH=release/bin elif [ "$BUILD_TYPE" == "debug-android" ]; then echo "Building debug for ANDROID : ultra INSECURE !!" - CONFIG="CONFIG+=debug qml_debug"; + CONFIG="CONFIG+=debug qml_debug WITH_SCANNER"; ANDROID=true BIN_PATH=debug/bin elif [ "$BUILD_TYPE" == "debug" ]; then diff --git a/components/QRCodeScanner.qml b/components/QRCodeScanner.qml new file mode 100644 index 00000000..10c1592a --- /dev/null +++ b/components/QRCodeScanner.qml @@ -0,0 +1,134 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.0 +import QtMultimedia 5.4 +import QtQuick.Dialogs 1.2 +import moneroComponents.QRCodeScanner 1.0 + +Rectangle { + id : root + + x: 0 + y: 0 + z: parent.z+1 + width: parent.width + height: parent.height + + visible: false + color: "black" + state: "Stopped" + + signal qrcode_decoded(string address, string payment_id, string amount, string tx_description, string recipient_name) + + states: [ + State { + name: "Capture" + StateChangeScript { + script: { + root.visible = true + camera.captureMode = Camera.CaptureStillImage + camera.start() + finder.enabled = true + } + } + }, + State { + name: "Stopped" + StateChangeScript { + script: { + camera.stop() + root.visible = false + finder.enabled = false + } + } + } + ] + + Camera { + id: camera + objectName: "qrCameraQML" + captureMode: Camera.CaptureStillImage + + focus { + focusMode: Camera.FocusContinuous + } + } + QRCodeScanner { + id : finder + objectName: "QrFinder" + onDecoded : { + root.qrcode_decoded(address, payment_id, amount, tx_description, recipient_name) + root.state = "Stopped" + } + onNotifyError : { + if( warning ) + messageDialog.icon = StandardIcon.Critical + else { + messageDialog.icon = StandardIcon.Warning + root.state = "Stopped" + } + messageDialog.text = error + messageDialog.visible = true + } + } + + VideoOutput { + id: viewfinder + visible: root.state == "Capture" + + x: 0 + y: 0 + z: parent.z+1 + width: parent.width + height: parent.height + + source: camera + autoOrientation: true + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + onPressAndHold: { + if (camera.lockStatus == Camera.locked)camera.unlock() + camera.searchAndLock() + } + onDoubleClicked: { + root.state = "Stopped" + } + } + } + + MessageDialog { + id: messageDialog + title: "Scanning QrCode" + onAccepted: { + root.state = "Stopped" + } + } +} diff --git a/main.cpp b/main.cpp index 232f4e15..83e48546 100644 --- a/main.cpp +++ b/main.cpp @@ -39,6 +39,7 @@ #include "WalletManager.h" #include "Wallet.h" #include "QRCodeImageProvider.h" +#include "QrCodeScanner.h" #include "PendingTransaction.h" #include "UnsignedTransaction.h" #include "TranslationManager.h" @@ -109,6 +110,8 @@ int main(int argc, char *argv[]) qRegisterMetaType(); qRegisterMetaType(); + qmlRegisterType("moneroComponents.QRCodeScanner", 1, 0, "QRCodeScanner"); + QQmlApplicationEngine engine; OSCursor cursor; @@ -167,5 +170,15 @@ int main(int argc, char *argv[]) //WalletManager::instance()->setLogLevel(WalletManager::LogLevel_Max); + bool builtWithScanner = false; +#ifdef WITH_SCANNER + builtWithScanner = true; + QObject *qmlCamera = rootObject->findChild("qrCameraQML"); + QCamera *camera_ = qvariant_cast(qmlCamera->property("mediaObject")); + QObject *qmlFinder = rootObject->findChild("QrFinder"); + qobject_cast(qmlFinder)->setSource(camera_); +#endif + engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner); + return app.exec(); } diff --git a/main.qml b/main.qml index e0f48142..affc90d3 100644 --- a/main.qml +++ b/main.qml @@ -32,6 +32,7 @@ import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Dialogs 1.2 import Qt.labs.settings 1.0 +import QtMultimedia 5.4 import moneroComponents.Wallet 1.0 import moneroComponents.PendingTransaction 1.0 @@ -65,6 +66,7 @@ ApplicationWindow { property bool viewOnly: false property bool foundNewBlock: false property int timeToUnlock: 0 + property bool qrScannerEnabled: builtWithScanner && (QtMultimedia.availableCameras.length > 0) // true if wallet ever synchronized property bool walletInitialized : false @@ -874,7 +876,10 @@ ApplicationWindow { messageText: qsTr("Please wait...") } - + QRCodeScanner { + id: cameraUi + visible : false + } Item { id: rootItem diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index e6a9a675..3066a296 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -1,6 +1,6 @@ TEMPLATE = app -QT += qml quick widgets +QT += qml quick widgets multimedia WALLET_ROOT=$$PWD/monero @@ -12,6 +12,7 @@ QMAKE_DISTCLEAN += -r $$WALLET_ROOT INCLUDEPATH += $$WALLET_ROOT/include \ $$PWD/src/libwalletqt \ $$PWD/src/QR-Code-generator \ + $$PWD/src/QR-Code-scanner \ $$PWD/src \ $$WALLET_ROOT/src @@ -37,8 +38,8 @@ HEADERS += \ src/model/AddressBookModel.h \ src/libwalletqt/AddressBook.h \ src/zxcvbn-c/zxcvbn.h \ - src/libwalletqt/UnsignedTransaction.h - + src/libwalletqt/UnsignedTransaction.h \ + src/QR-Code-scanner/QrCodeScanner.h SOURCES += main.cpp \ filter.cpp \ @@ -61,7 +62,8 @@ SOURCES += main.cpp \ src/model/AddressBookModel.cpp \ src/libwalletqt/AddressBook.cpp \ src/zxcvbn-c/zxcvbn.c \ - src/libwalletqt/UnsignedTransaction.cpp + src/libwalletqt/UnsignedTransaction.cpp \ + src/QR-Code-scanner/QrCodeScanner.cpp lupdate_only { SOURCES = *.qml \ @@ -76,6 +78,22 @@ LIBS += -L$$WALLET_ROOT/lib \ $$WALLET_ROOT/build/release/contrib/epee/src/libepee.a \ -lunbound +CONFIG(WITH_SCANNER) { + if( greaterThan(QT_MINOR_VERSION, 5) ) { + message("using camera scanner") + DEFINES += "WITH_SCANNER" + HEADERS += src/QR-Code-scanner/QrScanThread.h + SOURCES += src/QR-Code-scanner/QrScanThread.cpp + android { + INCLUDEPATH += $$PWD/../ZBar/include + LIBS += -lzbarjni -liconv + } else { + LIBS += -lzbar + } + } else { + message("Skipping camera scanner because of Incompatible Qt Version !") + } +} # currently we only support x86 build as qt.io only provides prebuilt qt for x86 mingw diff --git a/pages/AddressBook.qml b/pages/AddressBook.qml index 11086b99..d19b1287 100644 --- a/pages/AddressBook.qml +++ b/pages/AddressBook.qml @@ -62,12 +62,31 @@ Rectangle { tipText: qsTr("Tip tekst test") + translationManager.emptyString } + StandardButton { + id: qrfinderButton + anchors.left: parent.left + anchors.leftMargin: 17 + anchors.topMargin: 5 + anchors.top: addressLabel.bottom + text: qsTr("QRCODE") + translationManager.emptyString + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + visible : appWindow.qrScannerEnabled + enabled : visible + width: visible ? 60 : 0 + onClicked: { + cameraUi.state = "Capture" + cameraUi.qrcode_decoded.connect(updateFromQrCode) + } + } + LineEdit { id: addressLine - anchors.left: parent.left + anchors.left: qrfinderButton.right anchors.right: parent.right anchors.top: addressLabel.bottom - anchors.leftMargin: 17 anchors.rightMargin: 17 anchors.topMargin: 5 error: true; @@ -275,5 +294,13 @@ Rectangle { root.model = currentWallet.addressBookModel; } + function updateFromQrCode(address, payment_id, amount, tx_description, recipient_name) { + console.log("updateFromQrCode") + addressLine.text = address + paymentIdLine.text = payment_id + //amountLine.text = amount + descriptionLine.text = recipient_name + " " + tx_description + cameraUi.qrcode_decoded.disconnect(updateFromQrCode) + } } diff --git a/pages/Transfer.qml b/pages/Transfer.qml index 74e5d114..9df4c4a3 100644 --- a/pages/Transfer.qml +++ b/pages/Transfer.qml @@ -77,6 +77,15 @@ Rectangle { privacyLabel.text = qsTr("Privacy level (mixin %1)").arg(mixin) + translationManager.emptyString } + function updateFromQrCode(address, payment_id, amount, tx_description, recipient_name) { + console.log("updateFromQrCode") + addressLine.text = address + paymentIdLine.text = payment_id + amountLine.text = amount + descriptionLine.text = recipient_name + " " + tx_description + cameraUi.qrcode_decoded.disconnect(updateFromQrCode) + } + // Information dialog StandardDialog { // dynamically change onclose handler @@ -249,11 +258,29 @@ Rectangle { anchors.right: parent.right anchors.top: addressLabel.bottom + StandardButton { + id: qrfinderButton + anchors.left: parent.left + anchors.leftMargin: 17 + anchors.topMargin: 5 + text: qsTr("QRCODE") + translationManager.emptyString + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#FF6C3C" + pressedColor: "#FF4304" + visible : appWindow.qrScannerEnabled + enabled : visible + width: visible ? 60 : 0 + onClicked: { + cameraUi.state = "Capture" + cameraUi.qrcode_decoded.connect(updateFromQrCode) + } + } LineEdit { id: addressLine - anchors.left: parent.left + anchors.left: qrfinderButton.right anchors.right: resolveButton.left - anchors.leftMargin: 17 + //anchors.leftMargin: 17 anchors.topMargin: 5 placeholderText: "4..." // validator: RegExpValidator { regExp: /[0-9A-Fa-f]{95}/g } diff --git a/qml.qrc b/qml.qrc index 630826d9..c7b67fdd 100644 --- a/qml.qrc +++ b/qml.qrc @@ -125,5 +125,6 @@ wizard/WizardPasswordUI.qml wizard/WizardCreateViewOnlyWallet.qml components/DaemonConsole.qml + components/QRCodeScanner.qml diff --git a/src/QR-Code-scanner/QrCodeScanner.cpp b/src/QR-Code-scanner/QrCodeScanner.cpp new file mode 100644 index 00000000..94102429 --- /dev/null +++ b/src/QR-Code-scanner/QrCodeScanner.cpp @@ -0,0 +1,108 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QrCodeScanner.h" +#include +#include +#include + +QrCodeScanner::QrCodeScanner(QObject *parent) + : QObject(parent) + , m_processTimerId(-1) + , m_processInterval(750) + , m_enabled(true) +{ +#ifdef WITH_SCANNER + m_probe = new QVideoProbe(this); + m_thread = new QrScanThread(this); + m_thread->start(); + QObject::connect(m_thread, SIGNAL(decoded(int,QString)), this, SLOT(processCode(int,QString))); + QObject::connect(m_thread, SIGNAL(notifyError(const QString &, bool)), this, SIGNAL(notifyError(const QString &, bool))); + connect(m_probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame))); +#endif +} +void QrCodeScanner::setSource(QCamera *camera) +{ + m_probe->setSource(camera); +} +void QrCodeScanner::processCode(int type, const QString &data) +{ + if (! m_enabled) return; + qDebug() << "decoded - type: " << type << " data: " << data; + QString address, payment_id, tx_description, recipient_name, error; + QVector unknown_parameters; + uint64_t amount(0); + if( ! WalletManager::instance()->parse_uri(data, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error) ) + { + qDebug() << "Failed to parse_uri : " << error; + emit notifyError(error); + return; + } + if(unknown_parameters.size() > 0) + { + qDebug() << "unknown parameters " << unknown_parameters; + emit notifyError(error, true); + } + qDebug() << "Parsed URI : " << address << " " << payment_id << " " << amount << " " << tx_description << " " << recipient_name << " " << error; + QString s_amount = WalletManager::instance()->displayAmount(amount); + qDebug() << "Amount passed " << s_amount ; + emit decoded(address, payment_id, s_amount, tx_description, recipient_name); +} +void QrCodeScanner::processFrame(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(); +} +#ifdef WITH_SCANNER +void QrCodeScanner::timerEvent(QTimerEvent *event) +{ + if( (event->timerId() == m_processTimerId) ){ + m_thread->addFrame(m_curFrame); + } +} +#endif + diff --git a/src/QR-Code-scanner/QrCodeScanner.h b/src/QR-Code-scanner/QrCodeScanner.h new file mode 100644 index 00000000..791800f0 --- /dev/null +++ b/src/QR-Code-scanner/QrCodeScanner.h @@ -0,0 +1,79 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QRCODESCANNER_H_ +#define QRCODESCANNER_H_ + +#include +#include +#ifdef WITH_SCANNER +#include "QrScanThread.h" +#endif + +class QVideoProbe; +class QCamera; + +class QrCodeScanner : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + +public: + QrCodeScanner(QObject *parent = Q_NULLPTR); + + void setSource(QCamera*); + + bool enabled() const; + void setEnabled(bool enabled); + +public Q_SLOTS: + void processCode(int type, const QString &data); + void processFrame(QVideoFrame); + +Q_SIGNALS: + void enabledChanged(); + + void decoded(const QString &address, const QString &payment_id, const QString &amount, const QString &tx_description, const QString &recipient_name); + void decode(int type, const QString &data); + void notifyError(const QString &error, bool warning = false); + +protected: +#ifdef WITH_SCANNER + void timerEvent(QTimerEvent *); + QrScanThread *m_thread; +#endif + int m_processTimerId; + int m_processInterval; + int m_enabled; + QVideoFrame m_curFrame; + QVideoProbe *m_probe; +}; + +#endif + diff --git a/src/QR-Code-scanner/QrScanThread.cpp b/src/QR-Code-scanner/QrScanThread.cpp new file mode 100644 index 00000000..b6e9b9ff --- /dev/null +++ b/src/QR-Code-scanner/QrScanThread.cpp @@ -0,0 +1,132 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QrScanThread.h" +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +extern QImage qt_imageFromVideoFrame(const QVideoFrame &f); +#else +QImage qt_imageFromVideoFrame(const QVideoFrame &f){ + Q_ASSERT_X(0 != 0, "qt_imageFromVideoFrame", "Should have been managed in .pro"); + return QImage(); +} +#endif + +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(*(long*)"Y800"); + 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 : + 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.byteCount(); + dst.set_data(qimg.bits(), datalen); + if((width * 4 != bpl) || (width * height * 4 > datalen)){ + emit notifyError(QString("QImage to Zbar::Image failed !")); + return false; + } + return true; +} +void QrScanThread::processQImage(const QImage &qimg) +{ + try { + m_image = QSharedPointer(new zbar::Image()); + if( ! zimageFromQImage(qimg, *m_image) ) + return; + processZImage(*m_image); + } + catch(std::exception &e) { + qDebug() << "ERROR: " << e.what(); + emit notifyError(e.what()); + } +} + +void QrScanThread::processVideoFrame(const QVideoFrame &frame) +{ + processQImage( qt_imageFromVideoFrame(frame) ); +} + +void QrScanThread::stop() +{ + m_running = false; +} + +void QrScanThread::addFrame(const QVideoFrame &frame) +{ + QMutexLocker locker(&m_mutex); + m_queue.append(frame); + m_waitCondition.wakeOne(); +} + +void QrScanThread::run() +{ + QVideoFrame frame; + while(m_running) { + QMutexLocker locker(&m_mutex); + while(m_queue.isEmpty()) + m_waitCondition.wait(&m_mutex); + processVideoFrame(m_queue.takeFirst()); + } +} + diff --git a/src/QR-Code-scanner/QrScanThread.h b/src/QR-Code-scanner/QrScanThread.h new file mode 100644 index 00000000..03f17be3 --- /dev/null +++ b/src/QR-Code-scanner/QrScanThread.h @@ -0,0 +1,69 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef _QRSCANTHREAD_H_ +#define _QRSCANTHREAD_H_ + +#include +#include +#include +#include +#include +#include +#include + +class QrScanThread : public QThread, public zbar::Image::Handler +{ + Q_OBJECT + +public: + QrScanThread(QObject *parent = Q_NULLPTR); + void addFrame(const QVideoFrame &frame); + +Q_SIGNALS: + void decoded(int type, const QString &data); + void notifyError(const QString &error, bool warning = false); + +protected: + virtual void run(); + virtual void stop(); + void processVideoFrame(const QVideoFrame &); + void processQImage(const QImage &); + void processZImage(zbar::Image &image); + virtual void image_callback(zbar::Image &image); + bool zimageFromQImage(const QImage&, zbar::Image &); + +private: + zbar::ImageScanner m_scanner; + QSharedPointer m_image; + bool m_running; + QMutex m_mutex; + QWaitCondition m_waitCondition; + QList m_queue; +}; +#endif