QR-Code-scanner: integrate QUIRC library, implement QrDecoder, drop ZBar

This commit is contained in:
xiphon 2020-12-21 19:58:27 +00:00
parent df771470c2
commit 8d4cda030e
15 changed files with 140 additions and 149 deletions

3
.gitmodules vendored
View file

@ -2,3 +2,6 @@
path = monero path = monero
url = https://github.com/monero-project/monero url = https://github.com/monero-project/monero
ignore = all ignore = all
[submodule "external/quirc"]
path = external/quirc
url = https://github.com/dlbeer/quirc/

View file

@ -131,12 +131,8 @@ message(STATUS "OpenSSL: Version ${OPENSSL_VERSION}")
message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}") message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}")
message(STATUS "OpenSSL: libraries at ${OPENSSL_LIBRARIES} ${OPENSSL_SSL_LIBRARIES}") message(STATUS "OpenSSL: libraries at ${OPENSSL_LIBRARIES} ${OPENSSL_SSL_LIBRARIES}")
# Zbar (for QR scanner)
if(WITH_SCANNER) if(WITH_SCANNER)
add_definitions(-DWITH_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() endif()
# Sodium # Sodium
@ -563,6 +559,6 @@ if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
endif() endif()
endif() endif()
add_subdirectory(external)
add_subdirectory(translations) add_subdirectory(translations)
add_subdirectory(src) add_subdirectory(src)

View file

@ -151,25 +151,6 @@ RUN set -ex \
&& make -j${THREADS} install \ && make -j${THREADS} install \
&& rm -rf $(pwd) && 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 \ RUN git clone -b libgpg-error-1.38 --depth 1 git://git.gnupg.org/libgpg-error.git \
&& cd libgpg-error \ && cd libgpg-error \
&& git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 \ && git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 \

View file

@ -217,13 +217,13 @@ The following instructions will fetch Qt from your distribution's repositories i
- For Ubuntu - For Ubuntu
`sudo apt install qtmultimedia5-dev qml-module-qtmultimedia libzbar-dev` `sudo apt install qtmultimedia5-dev qml-module-qtmultimedia`
- For Gentoo - For Gentoo
The *qml* USE flag must be enabled. The *qml* USE flag must be enabled.
`emerge dev-qt/qtmultimedia:5 media-gfx/zbar` `emerge dev-qt/qtmultimedia:5`
3. Clone repository 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 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. 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 4. Install Qt5

View file

@ -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}")

7
external/CMakeLists.txt vendored Normal file
View file

@ -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)

1
external/quirc vendored Submodule

@ -0,0 +1 @@
Subproject commit 7e7ab596e4d0988faf1c12ae89c354b114181c40

View file

@ -55,13 +55,6 @@ if(ENABLE_PASS_STRENGTH_METER)
) )
endif() endif()
if(WITH_SCANNER)
file(GLOB QR_CODE_FILES
"QR-Code-scanner/*.h"
"QR-Code-scanner/*.cpp"
)
endif()
set(EXECUTABLE_FLAG) set(EXECUTABLE_FLAG)
if(MINGW) if(MINGW)
set(EXECUTABLE_FLAG WIN32) set(EXECUTABLE_FLAG WIN32)
@ -84,7 +77,6 @@ endif()
set(monero_wallet_gui_sources set(monero_wallet_gui_sources
${SOURCE_FILES} ${SOURCE_FILES}
${PASS_STRENGTH_FILES} ${PASS_STRENGTH_FILES}
${QR_CODE_FILES}
${RESOURCES} ${RESOURCES}
) )
@ -128,7 +120,6 @@ target_include_directories(monero-wallet-gui PUBLIC
${X11_INCLUDE_DIR} ${X11_INCLUDE_DIR}
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}
${ZBAR_INCLUDE_DIR}
) )
target_compile_definitions(monero-wallet-gui target_compile_definitions(monero-wallet-gui
@ -159,6 +150,7 @@ target_link_libraries(monero-wallet-gui
${EXTRA_LIBRARIES} ${EXTRA_LIBRARIES}
${ICU_LIBRARIES} ${ICU_LIBRARIES}
openpgp openpgp
qrdecoder
translations translations
) )
@ -171,16 +163,14 @@ if(X11_FOUND)
endif() endif()
if(WITH_SCANNER) if(WITH_SCANNER)
if(NOT ANDROID) target_link_libraries(monero-wallet-gui qrscanner)
if(LINUX AND NOT ANDROID)
target_link_libraries(monero-wallet-gui target_link_libraries(monero-wallet-gui
${ZBAR_LIBRARIES}
jpeg jpeg
v4l2 v4l2
v4lconvert v4lconvert
rt rt
) )
else()
target_link_libraries(monero-wallet-gui ${ZBAR_LIBRARIES})
endif() endif()
endif() endif()

View file

@ -1,4 +1,21 @@
file(GLOB_RECURSE SRC_SOURCES *.cpp) add_library(qrdecoder STATIC
file(GLOB_RECURSE SRC_HEADERS *.h) 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()

View file

@ -0,0 +1,71 @@
#include "Decoder.h"
#include <limits>
#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<std::string> QrDecoder::decode(const QImage &image)
{
if (image.format() == QImage::Format_Grayscale8)
{
return decodeGrayscale8(image);
}
return decodeGrayscale8(image.convertToFormat(QImage::Format_Grayscale8));
}
std::vector<std::string> 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<std::string> result;
result.reserve(static_cast<size_t>(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;
}

View file

@ -0,0 +1,21 @@
#include <QImage>
struct quirc;
class QrDecoder
{
public:
QrDecoder(const QrDecoder &) = delete;
QrDecoder &operator=(const QrDecoder &) = delete;
QrDecoder();
~QrDecoder();
std::vector<std::string> decode(const QImage &image);
private:
std::vector<std::string> decodeGrayscale8(const QImage &image);
private:
quirc *m_qr;
};

View file

@ -27,7 +27,6 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "QrCodeScanner.h" #include "QrCodeScanner.h"
#include <WalletManager.h>
#include <QVideoProbe> #include <QVideoProbe>
#include <QCamera> #include <QCamera>
@ -40,7 +39,7 @@ QrCodeScanner::QrCodeScanner(QObject *parent)
m_probe = new QVideoProbe(this); m_probe = new QVideoProbe(this);
m_thread = new QrScanThread(this); m_thread = new QrScanThread(this);
m_thread->start(); 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))); 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))); connect(m_probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame)));
} }

View file

@ -56,8 +56,7 @@ public Q_SLOTS:
Q_SIGNALS: Q_SIGNALS:
void enabledChanged(); void enabledChanged();
void decoded(int type, const QString &data); void decoded(const QString &data);
void decode(int type, const QString &data);
void notifyError(const QString &error, bool warning = false); void notifyError(const QString &error, bool warning = false);
protected: protected:

View file

@ -38,62 +38,15 @@ QrScanThread::QrScanThread(QObject *parent)
: QThread(parent) : QThread(parent)
,m_running(true) ,m_running(true)
{ {
m_scanner.set_handler(*this);
} }
void QrScanThread::image_callback(zbar::Image &image)
{
qDebug() << "image_callback : Found Code ! " ;
for(zbar::Image::SymbolIterator sym = image.symbol_begin();
sym != image.symbol_end();
++sym)
if(!sym->get_count()) {
QString data = QString::fromStdString(sym->get_data());
emit decoded(sym->get_type(), data);
}
}
void QrScanThread::processZImage(zbar::Image &image)
{
m_scanner.recycle_image(image);
zbar::Image tmp = image.convert(*(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) void QrScanThread::processQImage(const QImage &qimg)
{ {
try { try {
m_image = QSharedPointer<zbar::Image>(new zbar::Image()); for (const std::string &code : m_decoder.decode(qimg))
if( ! zimageFromQImage(qimg, *m_image) ) {
return; emit decoded(QString::fromStdString(code));
processZImage(*m_image); }
} }
catch(std::exception &e) { catch(std::exception &e) {
qDebug() << "ERROR: " << e.what(); qDebug() << "ERROR: " << e.what();

View file

@ -35,9 +35,10 @@
#include <QEvent> #include <QEvent>
#include <QVideoFrame> #include <QVideoFrame>
#include <QCamera> #include <QCamera>
#include <zbar.h>
class QrScanThread : public QThread, public zbar::Image::Handler #include "Decoder.h"
class QrScanThread : public QThread
{ {
Q_OBJECT Q_OBJECT
@ -47,20 +48,16 @@ public:
virtual void stop(); virtual void stop();
Q_SIGNALS: Q_SIGNALS:
void decoded(int type, const QString &data); void decoded(const QString &data);
void notifyError(const QString &error, bool warning = false); void notifyError(const QString &error, bool warning = false);
protected: protected:
virtual void run(); virtual void run();
void processVideoFrame(const QVideoFrame &); void processVideoFrame(const QVideoFrame &);
void processQImage(const QImage &); void processQImage(const QImage &);
void processZImage(zbar::Image &image);
virtual void image_callback(zbar::Image &image);
bool zimageFromQImage(const QImage&, zbar::Image &);
private: private:
zbar::ImageScanner m_scanner; QrDecoder m_decoder;
QSharedPointer<zbar::Image> m_image;
bool m_running; bool m_running;
QMutex m_mutex; QMutex m_mutex;
QWaitCondition m_waitCondition; QWaitCondition m_waitCondition;