mirror of https://github.com/status-im/qzxing.git
Merge pull request #212 from ftylitak/qt6_2_multimedia_support
Qt 6.2 multimedia support
This commit is contained in:
commit
6ea2b31e26
97
README.md
97
README.md
|
@ -4,41 +4,41 @@ Qt/QML wrapper library for the [ZXing](https://github.com/zxing/zxing) barcode i
|
|||
|
||||
Supports barcode decoding for the following types:
|
||||
|
||||
- UPC-A
|
||||
- UPC-E
|
||||
- EAN-8
|
||||
- EAN-13
|
||||
- ITF
|
||||
- Code 39
|
||||
- Code 93
|
||||
- Code 128 (GS1)
|
||||
- Codabar
|
||||
- QR Code
|
||||
- Data Matrix
|
||||
- Aztec (beta)
|
||||
- PDF 417
|
||||
- UPC-A
|
||||
- UPC-E
|
||||
- EAN-8
|
||||
- EAN-13
|
||||
- ITF
|
||||
- Code 39
|
||||
- Code 93
|
||||
- Code 128 (GS1)
|
||||
- Codabar
|
||||
- QR Code
|
||||
- Data Matrix
|
||||
- Aztec (beta)
|
||||
- PDF 417
|
||||
|
||||
Supports barcode encoding for the following types:
|
||||
|
||||
- QR Code
|
||||
- QR Code
|
||||
|
||||
# Table of contents
|
||||
|
||||
1. [How to include](#howToInclude)
|
||||
1. [Embed the source code](#embedInSourceCode)
|
||||
1. [Compile the project as an external library](#externalLibrary)
|
||||
1. [Control dependencies](#controlDependencies)
|
||||
1. [QZXing (core)](#controlDependenciesCore)
|
||||
1. [QZXing (core + QML)](#controlDependenciesCoreQML)
|
||||
1. [QZXing + QZXingFilter](#controlDependenciesCoreQMLQZXingFilter)
|
||||
1. [Embed the source code](#embedInSourceCode)
|
||||
1. [Compile the project as an external library](#externalLibrary)
|
||||
1. [Control dependencies](#controlDependencies)
|
||||
1. [QZXing (core)](#controlDependenciesCore)
|
||||
1. [QZXing (core + QML)](#controlDependenciesCoreQML)
|
||||
1. [QZXing + QZXingFilter](#controlDependenciesCoreQMLQZXingFilter)
|
||||
1. [How to use](#howTo)
|
||||
1. [Decoding operation](#howToDecoding)
|
||||
1. [C++/Qt](#howToDecodingCPP)
|
||||
1. [Qt Quick](#howToDecodingQtQuick)
|
||||
1. [Encoding operation](#howToEncoding)
|
||||
1. [C++/Qt](#howToEncodingCPP)
|
||||
1. [Qt Quick](#howToEncodingQtQuick)
|
||||
1. [Encoded text format Information](#howToEncodingFormatExamples)
|
||||
1. [Decoding operation](#howToDecoding)
|
||||
1. [C++/Qt](#howToDecodingCPP)
|
||||
1. [Qt Quick](#howToDecodingQtQuick)
|
||||
1. [Encoding operation](#howToEncoding)
|
||||
1. [C++/Qt](#howToEncodingCPP)
|
||||
1. [Qt Quick](#howToEncodingQtQuick)
|
||||
1. [Encoded text format Information](#howToEncodingFormatExamples)
|
||||
1. [Unit test dependency](#unitTestDependency)
|
||||
1. [Qt 6 limitations](#qt6limitations)
|
||||
1. [Contact](#contact)
|
||||
|
@ -98,18 +98,22 @@ CONFIG += qzxing_qml
|
|||
|
||||
### QZXing + QZXingFilter
|
||||
|
||||
QZXing includes QZXingFilter, a QAbstractVideoFilter implementation to provide a mean of providing live feed to the decoding library. It automatically includes QML implementation as well.
|
||||
This option requires "multimedia" Qt module this is why it is considered as a separate configuration. It can be used by adding the folloing line to the .pro file of a project:
|
||||
QZXing includes QZXingFilter, an implementation to provide live feed to the decoding library. It automatically includes QML implementation as well.
|
||||
This option requires "multimedia" Qt module this is why it is considered as a separate configuration. It can be used by adding the following line to the .pro file of a project:
|
||||
|
||||
```qmake
|
||||
CONFIG += qzxing_multimedia
|
||||
```
|
||||
|
||||
For examples on how to use QZXingFilter, it is advised to see [QZXingLive](https://github.com/ftylitak/qzxing/tree/master/examples/QZXingLive) example project. For Qt 5.x versions check [main.qml](https://github.com/ftylitak/qzxing/tree/master/examples/QZXingLive/main.qml) file, whereas for Qt 6.2 (or newer) check [main_qt6_2.qml](https://github.com/ftylitak/qzxing/tree/master/examples/QZXingLive/main_qt6_2.qml).
|
||||
|
||||
(Pending task: a wiki page should be written to better explain the usage of the QZXingFilter component)
|
||||
|
||||
<a name="howTo"></a>
|
||||
|
||||
# How to use
|
||||
|
||||
Follows simple code snippets that brefly show the use of the library. For more details advise the examples included in the repository and the [wiki](https://github.com/ftylitak/qzxing/wiki).
|
||||
Follows simple code snippets that briefly show the use of the library. For more details advise the examples included in the repository and the [wiki](https://github.com/ftylitak/qzxing/wiki).
|
||||
|
||||
<a name="howToDecoding"></a>
|
||||
|
||||
|
@ -159,7 +163,7 @@ int main()
|
|||
The in the QML file
|
||||
|
||||
```qml
|
||||
import QZXing 3.2
|
||||
import QZXing 3.3
|
||||
|
||||
function decode(preview) {
|
||||
imageToDecode.source = preview
|
||||
|
@ -210,9 +214,9 @@ The encoding function has been written as static as it does not have any depende
|
|||
|
||||
Use the encoding function with its default settings:
|
||||
|
||||
- Format: QR Code
|
||||
- Size: 240x240
|
||||
- Error Correction Level: Low (L)
|
||||
- Format: QR Code
|
||||
- Size: 240x240
|
||||
- Error Correction Level: Low (L)
|
||||
|
||||
```cpp
|
||||
#include "QZXing.h"
|
||||
|
@ -250,7 +254,7 @@ QZXing::registerQMLImageProvider(engine);
|
|||
Default settings:
|
||||
|
||||
```qml
|
||||
import QZXing 3.2
|
||||
import QZXing 3.3
|
||||
|
||||
TextField {
|
||||
id: inputField
|
||||
|
@ -265,18 +269,18 @@ Image{
|
|||
|
||||
Or use the encoding function with the optional custom settings that are passed like URL query parameters:
|
||||
|
||||
| attribute name | value | description |
|
||||
| --------------- | ----------- | --------------------------------------------------------- |
|
||||
| border | true, false | image has border (white 1px) |
|
||||
| correctionLevel | L, M, Q, H | the error correction level |
|
||||
| format | qrcode | the encode formatter. Currently only QR Code. |
|
||||
| transparent | true, false | whether the black pixels are transparent |
|
||||
| explicitSize | int | if provided, it will be the size of the Qr rectangle |
|
||||
| attribute name | value | description |
|
||||
| --------------- | ----------- | ---------------------------------------------------- |
|
||||
| border | true, false | image has border (white 1px) |
|
||||
| correctionLevel | L, M, Q, H | the error correction level |
|
||||
| format | qrcode | the encode formatter. Currently only QR Code. |
|
||||
| transparent | true, false | whether the black pixels are transparent |
|
||||
| explicitSize | int | if provided, it will be the size of the Qr rectangle |
|
||||
|
||||
the size of the image can be adjusted by using the Image.sourceWidth and Image.sourceHeight properties of Image QML element.
|
||||
|
||||
```qml
|
||||
import QZXing 3.2
|
||||
import QZXing 3.3
|
||||
|
||||
TextField {
|
||||
id: inputField
|
||||
|
@ -324,13 +328,6 @@ After testing, it seems that QTextCodec, if used through core5compat in Qt 6, it
|
|||
To avoid the dependency of an extra module (that also does not work as supposed to), QTextCodec has been replaced by [QStringDecoder](https://doc.qt.io/qt-6/qstringdecoder.html) only when building for Qt 6.
|
||||
If QZXing if build for Qt 5, QTextCodec is used as it was.
|
||||
|
||||
## Multimedia (Video / Camera)
|
||||
|
||||
Qt Multimedia modules that includes the Camera item for QML and Video related operations for frame manipulation and live decoding are not supported in Qt 6 for the moment.
|
||||
To my knowledge, there is no specific replacement for this absent modules and I hope they get re-supported for Qt 6.
|
||||
|
||||
Thus, if building for Qt 5, everything works fine. If trying to used **qzxing_multimedia** configuration in your **pro** file, the project will fail (example: [QZXingLive](https://github.com/ftylitak/qzxing/tree/master/examples/QZXingLive) project).
|
||||
|
||||
<a name="contact"></a>
|
||||
|
||||
# Contact
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
|
||||
import QtQuick 1.1
|
||||
import DropArea 1.0
|
||||
import QZXing 3.2
|
||||
import QZXing 3.3
|
||||
|
||||
Rectangle {
|
||||
width: 360
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import QtQuick 2.0
|
||||
import QZXing 3.2
|
||||
import QZXing 3.3
|
||||
|
||||
Rectangle {
|
||||
width: 360
|
||||
|
|
|
@ -15,12 +15,10 @@ CONFIG(debug, debug|release) {
|
|||
}
|
||||
|
||||
HEADERS += \
|
||||
application.h \
|
||||
native.h
|
||||
application.h
|
||||
|
||||
SOURCES += main.cpp \
|
||||
application.cpp \
|
||||
native.cpp
|
||||
application.cpp
|
||||
|
||||
RESOURCES += qml.qrc
|
||||
|
||||
|
@ -33,28 +31,35 @@ include(../../src/QZXing-components.pri)
|
|||
include(deployment.pri)
|
||||
|
||||
android {
|
||||
QT += androidextras
|
||||
lessThan(QT_VERSION, 6.2) {
|
||||
HEADERS += \
|
||||
native.h
|
||||
|
||||
DISTFILES += \
|
||||
android/AndroidManifest.xml \
|
||||
android/gradle/wrapper/gradle-wrapper.jar \
|
||||
android/gradlew \
|
||||
android/res/values/libs.xml \
|
||||
android/build.gradle \
|
||||
android/gradle/wrapper/gradle-wrapper.properties \
|
||||
android/gradlew.bat \
|
||||
android/AndroidManifest.xml \
|
||||
android/gradle/wrapper/gradle-wrapper.jar \
|
||||
android/gradlew \
|
||||
android/res/values/libs.xml \
|
||||
android/build.gradle \
|
||||
android/gradle/wrapper/gradle-wrapper.properties \
|
||||
android/gradlew.bat
|
||||
SOURCES += \
|
||||
native.cpp
|
||||
|
||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
||||
QT += androidextras
|
||||
|
||||
DISTFILES += \
|
||||
android/AndroidManifest.xml \
|
||||
android/gradle/wrapper/gradle-wrapper.jar \
|
||||
android/gradlew \
|
||||
android/res/values/libs.xml \
|
||||
android/build.gradle \
|
||||
android/gradle/wrapper/gradle-wrapper.properties \
|
||||
android/gradlew.bat \
|
||||
android/AndroidManifest.xml \
|
||||
android/gradle/wrapper/gradle-wrapper.jar \
|
||||
android/gradlew \
|
||||
android/res/values/libs.xml \
|
||||
android/build.gradle \
|
||||
android/gradle/wrapper/gradle-wrapper.properties \
|
||||
android/gradlew.bat
|
||||
|
||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
||||
}
|
||||
}
|
||||
|
||||
else:ios {
|
||||
QMAKE_INFO_PLIST=Info.plist
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
#include "application.h"
|
||||
#include <QDebug>
|
||||
|
||||
#if QT_VERSION < 0x060000
|
||||
#include "native.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
|
||||
#if QT_VERSION < 0x060100
|
||||
#include <QAndroidJniObject>
|
||||
#include <QtAndroid>
|
||||
#else
|
||||
#include <QJniObject>
|
||||
#endif
|
||||
|
||||
#endif // Q_OS_ANDROID
|
||||
|
||||
Application::Application()
|
||||
|
@ -18,12 +28,18 @@ Application::Application()
|
|||
connect(this, &Application::onPermissionsDenied,
|
||||
this, &Application::initializeQML);
|
||||
|
||||
NativeHelpers::registerApplicationInstance(this);
|
||||
#if QT_VERSION < 0x060000
|
||||
NativeHelpers::registerApplicationInstance(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::initializeQML()
|
||||
{
|
||||
#if QT_VERSION < 0x060200
|
||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||
#else
|
||||
engine.load(QUrl(QStringLiteral("qrc:/main_qt6_2.qml")));
|
||||
#endif // QT_VERSION < 0x060200
|
||||
}
|
||||
|
||||
void Application::checkPermissions()
|
||||
|
@ -32,10 +48,14 @@ void Application::checkPermissions()
|
|||
//intentionally called in the C++ thread since it is blocking and will continue after the check
|
||||
qDebug() << "About to request permissions";
|
||||
|
||||
#if QT_VERSION < 0x060000
|
||||
QAndroidJniObject::callStaticMethod<void>("org/ftylitak/qzxing/Utilities",
|
||||
"requestQZXingPermissions",
|
||||
"(Landroid/app/Activity;)V",
|
||||
QtAndroid::androidActivity().object());
|
||||
#else
|
||||
emit onPermissionsGranted();
|
||||
#endif
|
||||
qDebug() << "Permissions granted";
|
||||
#else
|
||||
emit onPermissionsGranted();
|
||||
|
|
|
@ -4,7 +4,7 @@ import QtQuick.Controls 2.0
|
|||
import QtQuick.Layouts 1.1
|
||||
import QtMultimedia 5.5
|
||||
|
||||
import QZXing 3.2
|
||||
import QZXing 3.3
|
||||
|
||||
ApplicationWindow
|
||||
{
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Window 2.0
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtMultimedia
|
||||
|
||||
import QZXing 3.3
|
||||
|
||||
ApplicationWindow
|
||||
{
|
||||
id: window
|
||||
visible: true
|
||||
width: 640
|
||||
height: 480
|
||||
title: "Qt QZXing Filter Test"
|
||||
|
||||
property int detectedTags: 0
|
||||
property string lastTag: ""
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: bgRect
|
||||
color: "white"
|
||||
anchors.fill: videoOutput
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: text1
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: 20
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
z: 50
|
||||
text: "Tags detected: " + detectedTags
|
||||
}
|
||||
Text
|
||||
{
|
||||
id: fps
|
||||
font.pixelSize: 20
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
z: 50
|
||||
text: (1000 / zxingFilter.timePerFrameDecode).toFixed(0) + "fps"
|
||||
}
|
||||
|
||||
Camera
|
||||
{
|
||||
id:camera
|
||||
active: true
|
||||
focusMode: Camera.FocusModeAutoNear
|
||||
}
|
||||
|
||||
CaptureSession {
|
||||
camera: camera
|
||||
videoOutput: videoOutput
|
||||
}
|
||||
|
||||
VideoOutput
|
||||
{
|
||||
id: videoOutput
|
||||
anchors.top: text1.bottom
|
||||
anchors.bottom: text2.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
// fillMode: VideoOutput.Stretch
|
||||
|
||||
property double captureRectStartFactorX: 0.25
|
||||
property double captureRectStartFactorY: 0.25
|
||||
property double captureRectFactorWidth: 0.5
|
||||
property double captureRectFactorHeight: 0.5
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
camera.customFocusPoint = Qt.point(mouseX / width, mouseY / height);
|
||||
camera.focusMode = Camera.FocusModeManual;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: captureZone
|
||||
color: "red"
|
||||
opacity: 0.2
|
||||
width: parent.width * parent.captureRectFactorWidth
|
||||
height: parent.height * parent.captureRectFactorHeight
|
||||
x: parent.width * parent.captureRectStartFactorX
|
||||
y: parent.height * parent.captureRectStartFactorY
|
||||
}
|
||||
|
||||
Component.onCompleted: { camera.active = false; camera.active = true; }
|
||||
}
|
||||
|
||||
QZXingFilter
|
||||
{
|
||||
id: zxingFilter
|
||||
videoSink: videoOutput.videoSink
|
||||
orientation: videoOutput.orientation
|
||||
|
||||
captureRect: {
|
||||
videoOutput.sourceRect;
|
||||
return Qt.rect(videoOutput.sourceRect.width * videoOutput.captureRectStartFactorX,
|
||||
videoOutput.sourceRect.height * videoOutput.captureRectStartFactorY,
|
||||
videoOutput.sourceRect.width * videoOutput.captureRectFactorWidth,
|
||||
videoOutput.sourceRect.height * videoOutput.captureRectFactorHeight)
|
||||
}
|
||||
|
||||
decoder {
|
||||
enabledDecoders: QZXing.DecoderFormat_EAN_13 | QZXing.DecoderFormat_CODE_39 | QZXing.DecoderFormat_QR_CODE
|
||||
|
||||
onTagFound: {
|
||||
console.log(tag + " | " + decoder.foundedFormat() + " | " + decoder.charSet());
|
||||
|
||||
window.detectedTags++;
|
||||
window.lastTag = tag;
|
||||
}
|
||||
|
||||
tryHarder: false
|
||||
}
|
||||
|
||||
onDecodingStarted:
|
||||
{
|
||||
// console.log("started");
|
||||
}
|
||||
|
||||
property int framesDecoded: 0
|
||||
property real timePerFrameDecode: 0
|
||||
|
||||
onDecodingFinished:
|
||||
{
|
||||
timePerFrameDecode = (decodeTime + framesDecoded * timePerFrameDecode) / (framesDecoded + 1);
|
||||
framesDecoded++;
|
||||
if(succeeded)
|
||||
console.log("frame finished: " + succeeded, decodeTime, timePerFrameDecode, framesDecoded);
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: text2
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: 20
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
z: 50
|
||||
text: "Last tag: " + lastTag
|
||||
}
|
||||
Switch {
|
||||
text: "Autofocus"
|
||||
checked: camera.focusMode === Camera.FocusModeAutoNear
|
||||
anchors {
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
camera.focusMode = Camera.FocusModeAutoNear
|
||||
} else {
|
||||
camera.focusMode = Camera.FocusModeManual
|
||||
camera.customFocusPoint = Qt.point(0.5, 0.5)
|
||||
}
|
||||
}
|
||||
font.family: Qt.platform.os === 'android' ? 'Droid Sans Mono' : 'Monospace'
|
||||
font.pixelSize: Screen.pixelDensity * 5
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>main.qml</file>
|
||||
<file>main_qt6_2.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.2)
|
||||
cmake_minimum_required(VERSION 3.3)
|
||||
project(QZXing)
|
||||
|
||||
find_package(Qt5 COMPONENTS Core REQUIRED)
|
||||
|
|
|
@ -409,11 +409,20 @@ qzxing_multimedia {
|
|||
DEFINES += QZXING_MULTIMEDIA
|
||||
PRL_EXPORT_DEFINES += QZXING_MULTIMEDIA
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/QZXingFilter.h
|
||||
lessThan(QT_VERSION, 6.2) {
|
||||
HEADERS += \
|
||||
$$PWD/QZXingFilter.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/QZXingFilter.cpp
|
||||
}
|
||||
greaterThan(QT_VERSION, 6.1) {
|
||||
QT += concurrent
|
||||
HEADERS += \
|
||||
$$PWD/QZXingFilterVideoSink.h
|
||||
SOURCES += \
|
||||
$$PWD/QZXingFilter.cpp
|
||||
$$PWD/QZXingFilterVideoSink.cpp
|
||||
}
|
||||
}
|
||||
|
||||
qzxing_qml {
|
||||
|
|
|
@ -27,7 +27,11 @@
|
|||
#endif // ENABLE_ENCODER_QR_CODE
|
||||
|
||||
#ifdef QZXING_MULTIMEDIA
|
||||
#include "QZXingFilter.h"
|
||||
#if QT_VERSION >= 0x060200
|
||||
#include "QZXingFilterVideoSink.h"
|
||||
#else
|
||||
#include "QZXingFilter.h"
|
||||
#endif //QT_VERSION
|
||||
#endif //QZXING_MULTIMEDIA
|
||||
|
||||
#ifdef QZXING_QML
|
||||
|
@ -98,10 +102,10 @@ QZXing::QZXing(QZXing::DecoderFormat decodeHints, QObject *parent) : QObject(par
|
|||
#if QT_VERSION >= 0x040700
|
||||
void QZXing::registerQMLTypes()
|
||||
{
|
||||
qmlRegisterType<QZXing>("QZXing", 3, 2, "QZXing");
|
||||
qmlRegisterType<QZXing>("QZXing", 3, 3, "QZXing");
|
||||
|
||||
#ifdef QZXING_MULTIMEDIA
|
||||
qmlRegisterType<QZXingFilter>("QZXing", 3, 2, "QZXingFilter");
|
||||
qmlRegisterType<QZXingFilter>("QZXing", 3, 3, "QZXingFilter");
|
||||
#endif //QZXING_MULTIMEDIA
|
||||
|
||||
}
|
||||
|
@ -419,8 +423,8 @@ QString QZXing::decodeImage(const QImage &image, int maxWidth, int maxHeight, bo
|
|||
|
||||
if(image.isNull())
|
||||
{
|
||||
emit decodingFinished(false);
|
||||
processingTime = t.elapsed();
|
||||
emit decodingFinished(false);
|
||||
//qDebug() << "End decoding 1";
|
||||
return "";
|
||||
}
|
||||
|
@ -551,15 +555,14 @@ QString QZXing::decodeImage(const QImage &image, int maxWidth, int maxHeight, bo
|
|||
emit tagFoundAdvanced(string, decodedFormat, charSet_, rect);
|
||||
}catch(zxing::Exception &/*e*/){}
|
||||
}
|
||||
processingTime = t.elapsed();
|
||||
emit decodingFinished(true);
|
||||
//qDebug() << "End decoding 2";
|
||||
return string;
|
||||
}
|
||||
|
||||
processingTime = t.elapsed();
|
||||
emit error(errorMessage);
|
||||
emit decodingFinished(false);
|
||||
processingTime = t.elapsed();
|
||||
//qDebug() << "End decoding 3";
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ CONFIG += \
|
|||
#qzxing_qml \
|
||||
#qzxing_multimedia \
|
||||
|
||||
VERSION = 3.2
|
||||
VERSION = 3.3
|
||||
|
||||
TARGET = QZXing
|
||||
TEMPLATE = lib
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
#include "QZXingFilterVideoSink.h"
|
||||
#include <QDebug>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include "QZXingImageProvider.h"
|
||||
|
||||
QZXingFilter::QZXingFilter(QObject *parent)
|
||||
: QObject(parent)
|
||||
, decoder(QZXing::DecoderFormat_QR_CODE)
|
||||
, decoding(false)
|
||||
{
|
||||
/// Connecting signals to handlers that will send signals to QML
|
||||
connect(&decoder, &QZXing::decodingStarted,
|
||||
this, &QZXingFilter::handleDecodingStarted);
|
||||
connect(&decoder, &QZXing::decodingFinished,
|
||||
this, &QZXingFilter::handleDecodingFinished);
|
||||
}
|
||||
|
||||
QZXingFilter::~QZXingFilter()
|
||||
{
|
||||
if(!processThread.isFinished()) {
|
||||
processThread.cancel();
|
||||
processThread.waitForFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void QZXingFilter::handleDecodingStarted()
|
||||
{
|
||||
decoding = true;
|
||||
emit decodingStarted();
|
||||
emit isDecodingChanged();
|
||||
}
|
||||
|
||||
void QZXingFilter::handleDecodingFinished(bool succeeded)
|
||||
{
|
||||
decoding = false;
|
||||
emit decodingFinished(succeeded, decoder.getProcessTimeOfLastDecoding());
|
||||
emit isDecodingChanged();
|
||||
}
|
||||
|
||||
void QZXingFilter::setOrientation(int orientation)
|
||||
{
|
||||
if (orientation_ == orientation) {
|
||||
return;
|
||||
}
|
||||
|
||||
orientation_ = orientation;
|
||||
emit orientationChanged(orientation_);
|
||||
}
|
||||
|
||||
int QZXingFilter::orientation() const
|
||||
{
|
||||
return orientation_;
|
||||
}
|
||||
|
||||
void QZXingFilter::setVideoSink(QObject *videoSink){
|
||||
m_videoSink = qobject_cast<QVideoSink*>(videoSink);
|
||||
|
||||
connect(m_videoSink, &QVideoSink::videoFrameChanged, this, &QZXingFilter::processFrame);
|
||||
}
|
||||
|
||||
void QZXingFilter::processFrame(const QVideoFrame &frame) {
|
||||
#ifdef Q_OS_ANDROID
|
||||
m_videoSink->setRhi(nullptr); // https://bugreports.qt.io/browse/QTBUG-97789
|
||||
QVideoFrame f(frame);
|
||||
f.map(QVideoFrame::ReadOnly);
|
||||
#else
|
||||
const QVideoFrame &f = frame;
|
||||
#endif // Q_OS_ANDROID
|
||||
|
||||
if(!isDecoding() && processThread.isFinished()){
|
||||
decoding = true;
|
||||
|
||||
QImage image = f.toImage();
|
||||
processThread = QtConcurrent::run([=](){
|
||||
if(image.isNull())
|
||||
{
|
||||
qDebug() << "QZXingFilter error: Cant create image file to process.";
|
||||
decoding = false;
|
||||
return;
|
||||
}
|
||||
|
||||
QImage frameToProcess(image);
|
||||
const QRect& rect = captureRect.toRect();
|
||||
|
||||
if (captureRect.isValid() && frameToProcess.size() != rect.size()) {
|
||||
frameToProcess = image.copy(rect);
|
||||
}
|
||||
|
||||
if (!orientation_) {
|
||||
decoder.decodeImage(frameToProcess);
|
||||
} else {
|
||||
QTransform transformation;
|
||||
transformation.translate(frameToProcess.rect().center().x(), frameToProcess.rect().center().y());
|
||||
transformation.rotate(-orientation_);
|
||||
|
||||
QImage translatedImage = frameToProcess.transformed(transformation);
|
||||
|
||||
decoder.decodeImage(translatedImage);
|
||||
}
|
||||
|
||||
// static int i=0;
|
||||
// qDebug() << "image.size()" << frameToProcess.size();
|
||||
// qDebug() << "image.format()" << frameToProcess.format();
|
||||
// const QString path = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) + "/qrtest/test_" + QString::number(i++ % 100) + ".png";
|
||||
// qDebug() << "saving image" << i << "at:" << path << frameToProcess.save(path);
|
||||
|
||||
decoder.decodeImage(frameToProcess, frameToProcess.width(), frameToProcess.height());
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
f.unmap();
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2017 QZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef QZXingFilter_H
|
||||
#define QZXingFilter_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
#include <QFuture>
|
||||
#include "QZXing.h"
|
||||
#include <QtMultimedia/QVideoSink>
|
||||
#include <QtMultimedia/QVideoFrame>
|
||||
|
||||
/// Video filter is the filter that has to be registered in C++, instantiated and attached in QML
|
||||
class QZXingFilter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool decoding READ isDecoding NOTIFY isDecodingChanged)
|
||||
Q_PROPERTY(QZXing* decoder READ getDecoder)
|
||||
Q_PROPERTY(QRectF captureRect MEMBER captureRect NOTIFY captureRectChanged)
|
||||
Q_PROPERTY(QObject* videoSink WRITE setVideoSink)
|
||||
Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
|
||||
|
||||
signals:
|
||||
void isDecodingChanged();
|
||||
void decodingFinished(bool succeeded, int decodeTime);
|
||||
void decodingStarted();
|
||||
void captureRectChanged();
|
||||
void orientationChanged(int orientation);
|
||||
|
||||
private slots:
|
||||
void handleDecodingStarted();
|
||||
void handleDecodingFinished(bool succeeded);
|
||||
void processFrame(const QVideoFrame &frame);
|
||||
void setOrientation(int orientation);
|
||||
int orientation() const;
|
||||
|
||||
private: /// Attributes
|
||||
QZXing decoder;
|
||||
bool decoding;
|
||||
QRectF captureRect;
|
||||
int orientation_;
|
||||
|
||||
QVideoSink *m_videoSink;
|
||||
QFuture<void> processThread;
|
||||
|
||||
public: /// Methods
|
||||
explicit QZXingFilter(QObject *parent = 0);
|
||||
void setVideoSink(QObject *videoSink);
|
||||
virtual ~QZXingFilter();
|
||||
|
||||
bool isDecoding() {return decoding; }
|
||||
QZXing* getDecoder() { return &decoder; }
|
||||
};
|
||||
|
||||
#endif // QZXingFilter_H
|
Loading…
Reference in New Issue