From 8d4cda030e445d2b64cc327af125b538e014ecb0 Mon Sep 17 00:00:00 2001 From: xiphon Date: Mon, 21 Dec 2020 19:58:27 +0000 Subject: [PATCH] QR-Code-scanner: integrate QUIRC library, implement QrDecoder, drop ZBar --- .gitmodules | 3 ++ CMakeLists.txt | 6 +-- Dockerfile.android | 19 ------- README.md | 10 +--- cmake/FindZBar0.cmake | 38 -------------- external/CMakeLists.txt | 7 +++ external/quirc | 1 + src/CMakeLists.txt | 16 ++---- src/QR-Code-scanner/CMakeLists.txt | 23 +++++++-- src/QR-Code-scanner/Decoder.cpp | 71 +++++++++++++++++++++++++++ src/QR-Code-scanner/Decoder.h | 21 ++++++++ src/QR-Code-scanner/QrCodeScanner.cpp | 3 +- src/QR-Code-scanner/QrCodeScanner.h | 3 +- src/QR-Code-scanner/QrScanThread.cpp | 55 ++------------------- src/QR-Code-scanner/QrScanThread.h | 13 ++--- 15 files changed, 140 insertions(+), 149 deletions(-) delete mode 100644 cmake/FindZBar0.cmake create mode 100644 external/CMakeLists.txt create mode 160000 external/quirc create mode 100644 src/QR-Code-scanner/Decoder.cpp create mode 100644 src/QR-Code-scanner/Decoder.h diff --git a/.gitmodules b/.gitmodules index ac11bb78..ac846a03 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = monero url = https://github.com/monero-project/monero ignore = all +[submodule "external/quirc"] + path = external/quirc + url = https://github.com/dlbeer/quirc/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 05324ca6..97c7608f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,12 +131,8 @@ message(STATUS "OpenSSL: Version ${OPENSSL_VERSION}") message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}") message(STATUS "OpenSSL: libraries at ${OPENSSL_LIBRARIES} ${OPENSSL_SSL_LIBRARIES}") -# Zbar (for QR scanner) if(WITH_SCANNER) add_definitions(-DWITH_SCANNER) - find_package(ZBar0 REQUIRED) - message(STATUS "libzbar: include dir at ${ZBAR_INCLUDE_DIR}") - message(STATUS "libzbar: libraries at ${ZBAR_LIBRARIES}") endif() # Sodium @@ -563,6 +559,6 @@ if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED) endif() endif() +add_subdirectory(external) add_subdirectory(translations) - add_subdirectory(src) diff --git a/Dockerfile.android b/Dockerfile.android index b529978e..b0a3d3c9 100644 --- a/Dockerfile.android +++ b/Dockerfile.android @@ -151,25 +151,6 @@ RUN set -ex \ && make -j${THREADS} install \ && rm -rf $(pwd) -RUN git clone https://github.com/ZBar/ZBar.git --depth 1 \ - && cd ZBar \ - && git reset --hard 854a5d97059e395807091ac4d80c53f7968abb8f \ - && sed -i 's/SHARED/STATIC/' android/jni/Android.mk \ - && sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CPP_FEATURES := exceptions rtti features\n\0/' android/jni/Android.mk \ - && sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS := -Wno-multichar\n\0/' android/jni/Android.mk \ - && sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -D_ANDROID\n\0/' android/jni/Android.mk \ - && sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -DLIBDIR="\\".\\""\n\0/' android/jni/Android.mk \ - && sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -DBUILDING_LIBICONV\n\0/' android/jni/Android.mk \ - && sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -DBUILDING_LIBCHARSET\n\0/' android/jni/Android.mk \ - && sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -DIN_LIBRARY\n\0/' android/jni/Android.mk \ - && sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -fno-stack-protector\n\0/' android/jni/Android.mk \ - && echo "APP_ABI := arm64-v8a \nAPP_STL := c++_shared \nTARGET_PLATFORM := ${ANDROID_API} \nTARGET_ARCH_ABI := arm64-v8a \nAPP_CFLAGS += -target aarch64-none-linux-android -fexceptions -fstack-protector-strong -fno-limit-debug-info -mfloat-abi=softfp -fno-builtin-memmove -fno-omit-frame-pointer -fno-stack-protector\n" \ - >> android/jni/Application.mk \ - && cd android \ - && CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ${ANDROID_NDK_ROOT}/ndk-build ICONV_SRC=${WORKDIR}/libiconv-${ICONV_VERSION} -B V=1 NDK_APPLICATION_MK=jni/Application.mk \ - && cp obj/local/arm64-v8a/lib* ${PREFIX}/lib \ - && cp -r ../include/* ${PREFIX}/include - RUN git clone -b libgpg-error-1.38 --depth 1 git://git.gnupg.org/libgpg-error.git \ && cd libgpg-error \ && git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 \ diff --git a/README.md b/README.md index 1158ea67..dd52e630 100644 --- a/README.md +++ b/README.md @@ -217,13 +217,13 @@ The following instructions will fetch Qt from your distribution's repositories i - For Ubuntu - `sudo apt install qtmultimedia5-dev qml-module-qtmultimedia libzbar-dev` + `sudo apt install qtmultimedia5-dev qml-module-qtmultimedia` - For Gentoo The *qml* USE flag must be enabled. - `emerge dev-qt/qtmultimedia:5 media-gfx/zbar` + `emerge dev-qt/qtmultimedia:5` 3. Clone repository @@ -290,12 +290,6 @@ The Monero GUI on Windows is 64 bits only; 32-bit Windows GUI builds are not off pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb mingw-w64-x86_64-libgcrypt ``` - Optional : To build the flag `WITH_SCANNER` - - ``` - pacman -S mingw-w64-x86_64-zbar - ``` - You find more details about those dependencies in the [Monero documentation](https://github.com/monero-project/monero). Note that that there is no more need to compile Boost from source; like everything else, you can install it now with a MSYS2 package. 4. Install Qt5 diff --git a/cmake/FindZBar0.cmake b/cmake/FindZBar0.cmake deleted file mode 100644 index f97b2378..00000000 --- a/cmake/FindZBar0.cmake +++ /dev/null @@ -1,38 +0,0 @@ -# from http://code.google.com/p/low-cost-vision-2012/source/browse/CMakeModules/FindZBar0.cmake?name=2-helium-1&r=d61f248bd5565b3c086bf4769a04bfd98f7079df -# - Try to find ZBar -# This will define -# -# ZBAR_FOUND - -# ZBAR_LIBRARY_DIR - -# ZBAR_INCLUDE_DIR - -# ZBAR_LIBRARIES - -# - -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} PATH_SUFFIXES zbar) - endif() -endif() - -if(NOT ZBAR_LIBRARIES AND ANDROID) - find_library(ZBARJNI_LIBRARY NAMES zbarjni) - find_library(ICONV_LIBRARY NAMES iconv) - if(ZBARJNI_LIBRARY AND ICONV_LIBRARY) - set(ZBAR_LIBRARIES ${ZBARJNI_LIBRARY} ${ICONV_LIBRARY}) - 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}/zbar") - endif() -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(ZBAR DEFAULT_MSG ZBAR_LIBRARIES ZBAR_INCLUDE_DIR) -message(STATUS "Found zbar libraries ${ZBAR_LIBRARIES}") diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt new file mode 100644 index 00000000..c99cd900 --- /dev/null +++ b/external/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(quirc STATIC + quirc/lib/decode.c + quirc/lib/identify.c + quirc/lib/quirc.c + quirc/lib/version_db.c +) +target_include_directories(quirc PUBLIC quirc/lib) diff --git a/external/quirc b/external/quirc new file mode 160000 index 00000000..7e7ab596 --- /dev/null +++ b/external/quirc @@ -0,0 +1 @@ +Subproject commit 7e7ab596e4d0988faf1c12ae89c354b114181c40 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e38c7dde..a4c3a15b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -55,13 +55,6 @@ if(ENABLE_PASS_STRENGTH_METER) ) endif() -if(WITH_SCANNER) - file(GLOB QR_CODE_FILES - "QR-Code-scanner/*.h" - "QR-Code-scanner/*.cpp" - ) -endif() - set(EXECUTABLE_FLAG) if(MINGW) set(EXECUTABLE_FLAG WIN32) @@ -84,7 +77,6 @@ endif() set(monero_wallet_gui_sources ${SOURCE_FILES} ${PASS_STRENGTH_FILES} - ${QR_CODE_FILES} ${RESOURCES} ) @@ -128,7 +120,6 @@ target_include_directories(monero-wallet-gui PUBLIC ${X11_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} - ${ZBAR_INCLUDE_DIR} ) target_compile_definitions(monero-wallet-gui @@ -159,6 +150,7 @@ target_link_libraries(monero-wallet-gui ${EXTRA_LIBRARIES} ${ICU_LIBRARIES} openpgp + qrdecoder translations ) @@ -171,16 +163,14 @@ if(X11_FOUND) endif() if(WITH_SCANNER) - if(NOT ANDROID) + target_link_libraries(monero-wallet-gui qrscanner) + if(LINUX AND NOT ANDROID) target_link_libraries(monero-wallet-gui - ${ZBAR_LIBRARIES} jpeg v4l2 v4lconvert rt ) - else() - target_link_libraries(monero-wallet-gui ${ZBAR_LIBRARIES}) endif() endif() diff --git a/src/QR-Code-scanner/CMakeLists.txt b/src/QR-Code-scanner/CMakeLists.txt index 9ca31c61..15e288df 100644 --- a/src/QR-Code-scanner/CMakeLists.txt +++ b/src/QR-Code-scanner/CMakeLists.txt @@ -1,4 +1,21 @@ -file(GLOB_RECURSE SRC_SOURCES *.cpp) -file(GLOB_RECURSE SRC_HEADERS *.h) - +add_library(qrdecoder STATIC + Decoder.cpp +) +target_link_libraries(qrdecoder + PUBLIC + Qt5::Gui + PRIVATE + quirc +) +if(WITH_SCANNER) + add_library(qrscanner + QrCodeScanner.cpp + QrScanThread.cpp + ) + target_link_libraries(qrscanner + PUBLIC + Qt5::Multimedia + qrdecoder + ) +endif() diff --git a/src/QR-Code-scanner/Decoder.cpp b/src/QR-Code-scanner/Decoder.cpp new file mode 100644 index 00000000..a814969b --- /dev/null +++ b/src/QR-Code-scanner/Decoder.cpp @@ -0,0 +1,71 @@ +#include "Decoder.h" + +#include + +#include "quirc.h" + +QrDecoder::QrDecoder() + : m_qr(quirc_new()) +{ + if (m_qr == nullptr) + { + throw std::runtime_error("QUIRC: failed to allocate memory"); + } +} + +QrDecoder::~QrDecoder() +{ + quirc_destroy(m_qr); +} + +std::vector QrDecoder::decode(const QImage &image) +{ + if (image.format() == QImage::Format_Grayscale8) + { + return decodeGrayscale8(image); + } + return decodeGrayscale8(image.convertToFormat(QImage::Format_Grayscale8)); +} + +std::vector QrDecoder::decodeGrayscale8(const QImage &image) +{ + if (quirc_resize(m_qr, image.width(), image.height()) < 0) + { + throw std::runtime_error("QUIRC: failed to allocate video memory"); + } + + uint8_t *rawImage = quirc_begin(m_qr, nullptr, nullptr); + if (rawImage == nullptr) + { + throw std::runtime_error("QUIRC: failed to get image buffer"); + } +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + std::copy(image.constBits(), image.constBits() + image.sizeInBytes(), rawImage); +#else + std::copy(image.constBits(), image.constBits() + image.byteCount(), rawImage); +#endif + quirc_end(m_qr); + + const int count = quirc_count(m_qr); + if (count < 0) + { + throw std::runtime_error("QUIRC: failed to get the number of recognized QR-codes"); + } + + std::vector result; + result.reserve(static_cast(count)); + for (int index = 0; index < count; ++index) + { + quirc_code code; + quirc_extract(m_qr, index, &code); + + quirc_data data; + const quirc_decode_error_t err = quirc_decode(&code, &data); + if (err == QUIRC_SUCCESS) + { + result.emplace_back(&data.payload[0], &data.payload[data.payload_len]); + } + } + + return result; +} diff --git a/src/QR-Code-scanner/Decoder.h b/src/QR-Code-scanner/Decoder.h new file mode 100644 index 00000000..e25c3936 --- /dev/null +++ b/src/QR-Code-scanner/Decoder.h @@ -0,0 +1,21 @@ +#include + +struct quirc; + +class QrDecoder +{ +public: + QrDecoder(const QrDecoder &) = delete; + QrDecoder &operator=(const QrDecoder &) = delete; + + QrDecoder(); + ~QrDecoder(); + + std::vector decode(const QImage &image); + +private: + std::vector decodeGrayscale8(const QImage &image); + +private: + quirc *m_qr; +}; diff --git a/src/QR-Code-scanner/QrCodeScanner.cpp b/src/QR-Code-scanner/QrCodeScanner.cpp index 55e396fe..2e8bde9f 100644 --- a/src/QR-Code-scanner/QrCodeScanner.cpp +++ b/src/QR-Code-scanner/QrCodeScanner.cpp @@ -27,7 +27,6 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "QrCodeScanner.h" -#include #include #include @@ -40,7 +39,7 @@ QrCodeScanner::QrCodeScanner(QObject *parent) m_probe = new QVideoProbe(this); m_thread = new QrScanThread(this); m_thread->start(); - QObject::connect(m_thread, SIGNAL(decoded(int, QString)), this, SIGNAL(decoded(int, QString))); + QObject::connect(m_thread, SIGNAL(decoded(QString)), this, SIGNAL(decoded(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))); } diff --git a/src/QR-Code-scanner/QrCodeScanner.h b/src/QR-Code-scanner/QrCodeScanner.h index 9420693d..10cf7766 100644 --- a/src/QR-Code-scanner/QrCodeScanner.h +++ b/src/QR-Code-scanner/QrCodeScanner.h @@ -56,8 +56,7 @@ public Q_SLOTS: Q_SIGNALS: void enabledChanged(); - void decoded(int type, const QString &data); - void decode(int type, const QString &data); + void decoded(const QString &data); void notifyError(const QString &error, bool warning = false); protected: diff --git a/src/QR-Code-scanner/QrScanThread.cpp b/src/QR-Code-scanner/QrScanThread.cpp index 15f4c030..6421321e 100644 --- a/src/QR-Code-scanner/QrScanThread.cpp +++ b/src/QR-Code-scanner/QrScanThread.cpp @@ -38,62 +38,15 @@ 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"); -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - unsigned long datalen = qimg.sizeInBytes(); -#else - unsigned long datalen = qimg.byteCount(); -#endif - 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); + for (const std::string &code : m_decoder.decode(qimg)) + { + emit decoded(QString::fromStdString(code)); + } } catch(std::exception &e) { qDebug() << "ERROR: " << e.what(); diff --git a/src/QR-Code-scanner/QrScanThread.h b/src/QR-Code-scanner/QrScanThread.h index b6daa29f..7dfffc55 100644 --- a/src/QR-Code-scanner/QrScanThread.h +++ b/src/QR-Code-scanner/QrScanThread.h @@ -35,9 +35,10 @@ #include #include #include -#include -class QrScanThread : public QThread, public zbar::Image::Handler +#include "Decoder.h" + +class QrScanThread : public QThread { Q_OBJECT @@ -47,20 +48,16 @@ public: virtual void stop(); Q_SIGNALS: - void decoded(int type, const QString &data); + void decoded(const QString &data); void notifyError(const QString &error, bool warning = false); protected: virtual void run(); 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; + QrDecoder m_decoder; bool m_running; QMutex m_mutex; QWaitCondition m_waitCondition;