diff options
131 files changed, 6180 insertions, 1213 deletions
diff --git a/examples/corelib/threads/mandelbrot/mandelbrotwidget.cpp b/examples/corelib/threads/mandelbrot/mandelbrotwidget.cpp index 822791533b..420fd6186a 100644 --- a/examples/corelib/threads/mandelbrot/mandelbrotwidget.cpp +++ b/examples/corelib/threads/mandelbrot/mandelbrotwidget.cpp @@ -56,29 +56,28 @@ #include <math.h> //! [0] -const double DefaultCenterX = -0.637011f; -const double DefaultCenterY = -0.0395159f; -const double DefaultScale = 0.00403897f; +const double DefaultCenterX = -0.637011; +const double DefaultCenterY = -0.0395159; +const double DefaultScale = 0.00403897; -const double ZoomInFactor = 0.8f; +const double ZoomInFactor = 0.8; const double ZoomOutFactor = 1 / ZoomInFactor; const int ScrollStep = 20; //! [0] //! [1] -MandelbrotWidget::MandelbrotWidget(QWidget *parent) - : QWidget(parent) +MandelbrotWidget::MandelbrotWidget(QWidget *parent) : + QWidget(parent), + centerX(DefaultCenterX), + centerY(DefaultCenterY), + pixmapScale(DefaultScale), + curScale(DefaultScale) { - centerX = DefaultCenterX; - centerY = DefaultCenterY; - pixmapScale = DefaultScale; - curScale = DefaultScale; - connect(&thread, &RenderThread::renderedImage, this, &MandelbrotWidget::updatePixmap); setWindowTitle(tr("Mandelbrot")); -#ifndef QT_NO_CURSOR +#if QT_CONFIG(cursor) setCursor(Qt::CrossCursor); #endif resize(550, 400); @@ -102,7 +101,7 @@ void MandelbrotWidget::paintEvent(QPaintEvent * /* event */) //! [4] //! [5] - if (curScale == pixmapScale) { + if (qFuzzyCompare(curScale, pixmapScale)) { //! [5] //! [6] painter.drawPixmap(pixmapOffset, pixmap); //! [6] //! [7] @@ -176,8 +175,8 @@ void MandelbrotWidget::keyPressEvent(QKeyEvent *event) //! [12] void MandelbrotWidget::wheelEvent(QWheelEvent *event) { - int numDegrees = event->angleDelta().y() / 8; - double numSteps = numDegrees / 15.0f; + const int numDegrees = event->angleDelta().y() / 8; + const double numSteps = numDegrees / double(15); zoom(pow(ZoomInFactor, numSteps)); } //! [12] diff --git a/examples/corelib/threads/mandelbrot/renderthread.cpp b/examples/corelib/threads/mandelbrot/renderthread.cpp index eee44c7242..22ce81c32d 100644 --- a/examples/corelib/threads/mandelbrot/renderthread.cpp +++ b/examples/corelib/threads/mandelbrot/renderthread.cpp @@ -57,9 +57,6 @@ RenderThread::RenderThread(QObject *parent) : QThread(parent) { - restart = false; - abort = false; - for (int i = 0; i < ColormapSize; ++i) colormap[i] = rgbFromWaveLength(380.0 + (i * 400.0 / ColormapSize)); } @@ -102,10 +99,10 @@ void RenderThread::run() { forever { mutex.lock(); - QSize resultSize = this->resultSize; - double scaleFactor = this->scaleFactor; - double centerX = this->centerX; - double centerY = this->centerY; + const QSize resultSize = this->resultSize; + const double scaleFactor = this->scaleFactor; + const double centerX = this->centerX; + const double centerY = this->centerY; mutex.unlock(); //! [3] @@ -128,20 +125,20 @@ void RenderThread::run() if (abort) return; - uint *scanLine = + auto scanLine = reinterpret_cast<uint *>(image.scanLine(y + halfHeight)); - double ay = centerY + (y * scaleFactor); + const double ay = centerY + (y * scaleFactor); for (int x = -halfWidth; x < halfWidth; ++x) { - double ax = centerX + (x * scaleFactor); + const double ax = centerX + (x * scaleFactor); double a1 = ax; double b1 = ay; int numIterations = 0; do { ++numIterations; - double a2 = (a1 * a1) - (b1 * b1) + ax; - double b2 = (2 * a1 * b1) + ay; + const double a2 = (a1 * a1) - (b1 * b1) + ax; + const double b2 = (2 * a1 * b1) + ay; if ((a2 * a2) + (b2 * b2) > Limit) break; @@ -187,9 +184,9 @@ void RenderThread::run() //! [10] uint RenderThread::rgbFromWaveLength(double wave) { - double r = 0.0; - double g = 0.0; - double b = 0.0; + double r = 0; + double g = 0; + double b = 0; if (wave >= 380.0 && wave <= 440.0) { r = -1.0 * (wave - 440.0) / (440.0 - 380.0); diff --git a/examples/corelib/threads/mandelbrot/renderthread.h b/examples/corelib/threads/mandelbrot/renderthread.h index 4f0394d554..934cc17d74 100644 --- a/examples/corelib/threads/mandelbrot/renderthread.h +++ b/examples/corelib/threads/mandelbrot/renderthread.h @@ -78,7 +78,7 @@ protected: void run() override; private: - uint rgbFromWaveLength(double wave); + static uint rgbFromWaveLength(double wave); QMutex mutex; QWaitCondition condition; @@ -86,8 +86,8 @@ private: double centerY; double scaleFactor; QSize resultSize; - bool restart; - bool abort; + bool restart = false; + bool abort = false; enum { ColormapSize = 512 }; uint colormap[ColormapSize]; diff --git a/examples/opengl/paintedwindow/paintedwindow.cpp b/examples/opengl/paintedwindow/paintedwindow.cpp index 799431a765..94aa215288 100644 --- a/examples/opengl/paintedwindow/paintedwindow.cpp +++ b/examples/opengl/paintedwindow/paintedwindow.cpp @@ -54,6 +54,7 @@ #include <QOpenGLContext> #include <QOpenGLPaintDevice> #include <QPainter> +#include <QPainterPath> #include <QScreen> #include <QTimer> diff --git a/examples/opengl/qopenglwindow/main.cpp b/examples/opengl/qopenglwindow/main.cpp index 8932269ddf..7ce4dd0977 100644 --- a/examples/opengl/qopenglwindow/main.cpp +++ b/examples/opengl/qopenglwindow/main.cpp @@ -51,6 +51,7 @@ #include <QOpenGLWindow> #include <QScreen> #include <QPainter> +#include <QPainterPath> #include <QGuiApplication> #include <QMatrix4x4> #include <QStaticText> diff --git a/examples/qpa/qrasterwindow/main.cpp b/examples/qpa/qrasterwindow/main.cpp index 958da2205a..699c950c27 100644 --- a/examples/qpa/qrasterwindow/main.cpp +++ b/examples/qpa/qrasterwindow/main.cpp @@ -50,6 +50,7 @@ #include <QRasterWindow> #include <QPainter> +#include <QPainterPath> #include <QGuiApplication> #include <QMatrix4x4> #include <QTimer> diff --git a/examples/sql/doc/src/drilldown.qdoc b/examples/sql/doc/src/drilldown.qdoc index 8beb515a83..a7a9601c26 100644 --- a/examples/sql/doc/src/drilldown.qdoc +++ b/examples/sql/doc/src/drilldown.qdoc @@ -437,6 +437,9 @@ \snippet drilldown/imageitem.h 0 + We declare a \c Type enum value for our custom item and reimplement + \l{QGreaphicsItem::}{type()}. This is done so we can safely use + qgraphicsitem_cast(). In addition, we implement a public \c id() function to be able to identify the associated location and a public \c adjust() function that can be called to ensure that the image item is given the diff --git a/examples/sql/drilldown/imageitem.h b/examples/sql/drilldown/imageitem.h index abb9103c7e..324c847b12 100644 --- a/examples/sql/drilldown/imageitem.h +++ b/examples/sql/drilldown/imageitem.h @@ -60,8 +60,11 @@ class ImageItem : public QObject, public QGraphicsPixmapItem Q_OBJECT public: + enum { Type = UserType + 1 }; + ImageItem(int id, const QPixmap &pixmap, QGraphicsItem *parent = nullptr); + int type() const override { return Type; } void adjust(); int id() const; diff --git a/examples/widgets/painting/basicdrawing/renderarea.cpp b/examples/widgets/painting/basicdrawing/renderarea.cpp index a1437dea8f..09d610ee95 100644 --- a/examples/widgets/painting/basicdrawing/renderarea.cpp +++ b/examples/widgets/painting/basicdrawing/renderarea.cpp @@ -51,6 +51,7 @@ #include "renderarea.h" #include <QPainter> +#include <QPainterPath> //! [0] RenderArea::RenderArea(QWidget *parent) diff --git a/examples/widgets/widgets/styles/norwegianwoodstyle.cpp b/examples/widgets/widgets/styles/norwegianwoodstyle.cpp index 31150cd994..34a63e0eea 100644 --- a/examples/widgets/widgets/styles/norwegianwoodstyle.cpp +++ b/examples/widgets/widgets/styles/norwegianwoodstyle.cpp @@ -52,6 +52,7 @@ #include <QComboBox> #include <QPainter> +#include <QPainterPath> #include <QPushButton> #include <QStyleFactory> diff --git a/mkspecs/android-clang/qmake.conf b/mkspecs/android-clang/qmake.conf index 31ee5d2637..5cc5a20f71 100644 --- a/mkspecs/android-clang/qmake.conf +++ b/mkspecs/android-clang/qmake.conf @@ -43,6 +43,9 @@ isEmpty(ALL_ANDROID_ABIS): ALL_ANDROID_ABIS = arm64-v8a armeabi-v7a x86_64 x86 CONFIG += $$ANDROID_PLATFORM +ANDROID_MIN_SDK_VERSION = $$replace(ANDROID_PLATFORM, "android-", "") +ANDROID_TARGET_SDK_VERSION = 28 + NDK_LLVM_PATH = $$NDK_ROOT/toolchains/llvm/prebuilt/$$NDK_HOST QMAKE_CC = $$NDK_LLVM_PATH/bin/clang QMAKE_CXX = $$NDK_LLVM_PATH/bin/clang++ diff --git a/mkspecs/features/android/android_deployment_settings.prf b/mkspecs/features/android/android_deployment_settings.prf index f375a687a9..7cda5096b1 100644 --- a/mkspecs/features/android/android_deployment_settings.prf +++ b/mkspecs/features/android/android_deployment_settings.prf @@ -53,6 +53,12 @@ contains(TEMPLATE, ".*app"):!build_pass:!android-embedded { !isEmpty(ANDROID_VERSION_CODE): \ FILE_CONTENT += " \"android-version-code\": $$emitString($$ANDROID_VERSION_CODE)," + !isEmpty(ANDROID_MIN_SDK_VERSION): \ + FILE_CONTENT += " \"android-min-sdk-version\": $$emitString($$ANDROID_MIN_SDK_VERSION)," + + !isEmpty(ANDROID_TARGET_SDK_VERSION): \ + FILE_CONTENT += " \"android-target-sdk-version\": $$emitString($$ANDROID_TARGET_SDK_VERSION)," + !isEmpty(ANDROID_EXTRA_LIBS): \ FILE_CONTENT += " \"android-extra-libs\": $$emitString($$join(ANDROID_EXTRA_LIBS, ","))," diff --git a/qmake/doc/src/qmake-manual.qdoc b/qmake/doc/src/qmake-manual.qdoc index 1e1e23495c..351009bd1c 100644 --- a/qmake/doc/src/qmake-manual.qdoc +++ b/qmake/doc/src/qmake-manual.qdoc @@ -1111,6 +1111,15 @@ \row \li designer \li The target is a plugin for \QD. \row \li no_lflags_merge \li Ensures that the list of libraries stored in the \c LIBS variable is not reduced to a list of unique values before it is used. + \row \li metatypes \li Create a \c {<name>_metatypes.json} file for the + current project. \c {<name>} is the all lowercase base name of + \l TARGET. + \row \li qmltypes \li Automatically register QML types defined in C++. + For more information, see \l {Defining QML Types from C++}. + Also, create a \c {<template>.qmltypes} file for the current + project. \c <template> will be \c plugins (plural, for historical + reasons) if \c plugin is set, or the value of \l TEMPLATE otherwise. + \c qmltypes implies \c metatypes. \endtable These options define specific features on Windows only: @@ -2579,6 +2588,32 @@ For more information, see \l {Expressing Supported iOS Versions}. + \section1 QML_IMPORT_MAJOR_VERSION + + Specifies the major version to be used for automatically generated QML type + registrations. For more information, see \l {Defining QML Types from C++}. + + \section1 QML_IMPORT_MINOR_VERSION + + When automatically registering QML types defined in C++, register an + additional version of the module using this minor version. Generally, + minor versions to be registered are inferred from the meta objects. + + You can use this variable if the meta objects have not changed and you + still want to import a QML module with a newer minor version number. For + example, \c MyModule metaobjects are at \c 1.1 level, but you want to import + the module as \c 1.3. + + \section1 QML_IMPORT_VERSION + + Specifies \l QML_IMPORT_MAJOR_VERSION and \l QML_IMPORT_MINOR_VERSION as a + \c {<major>.<minor>} version string. + + \section1 QML_IMPORT_NAME + + Specifies the module name to be used for automatically generated QML type + registrations. For more information, see \l {Defining QML Types from C++}. + \section1 QT Specifies the \l{All Modules}{Qt modules} that are used by your project. For diff --git a/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties b/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties index 4b7e1f3d38..5028f28f8e 100644 --- a/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties +++ b/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/android/templates/AndroidManifest.xml b/src/android/templates/AndroidManifest.xml index 8dadd1eac8..e31dea6a8c 100644 --- a/src/android/templates/AndroidManifest.xml +++ b/src/android/templates/AndroidManifest.xml @@ -1,7 +1,5 @@ <?xml version='1.0' encoding='utf-8'?> <manifest package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto"> - <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="28"/> - <!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application. Remove the comment if you do not require these default permissions. --> <!-- %%INSERT_PERMISSIONS --> diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle index 3087d08c83..d5b3b93499 100644 --- a/src/android/templates/build.gradle +++ b/src/android/templates/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.6.0' } } @@ -59,4 +59,10 @@ android { aaptOptions { noCompress 'rcc' } + + defaultConfig { + resConfig "en" + minSdkVersion = qtMinSdkVersion + targetSdkVersion = qtTargetSdkVersion + } } diff --git a/src/corelib/Qt5AndroidSupport.cmake b/src/corelib/Qt5AndroidSupport.cmake index 5f24fb0e8c..4db38f3957 100644 --- a/src/corelib/Qt5AndroidSupport.cmake +++ b/src/corelib/Qt5AndroidSupport.cmake @@ -18,6 +18,8 @@ if (NOT ${PROJECT_NAME}-MultiAbiBuild) option(ANDROID_BUILD_ABI_${abi} "Enable the build for Android ${abi}" ${abi_initial_value}) endif() endforeach() + option(ANDROID_MIN_SDK_VERSION "Android minimum SDK version" "21") + option(ANDROID_TARGET_SDK_VERSION "Android target SDK version" "28") # Make sure to delete the "android-build" directory, which contains all the # build artefacts, and also the androiddeployqt/gradle artefacts @@ -101,6 +103,9 @@ if (NOT ${PROJECT_NAME}-MultiAbiBuild) generate_json_variable(ANDROID_VERSION_NAME "android-version-name") generate_json_variable_list(ANDROID_EXTRA_LIBS "android-extra-libs") generate_json_variable_list(QML_IMPORT_PATH "qml-import-paths") + generate_json_variable_list(ANDROID_MIN_SDK_VERSION "android-min-sdk-version") + generate_json_variable_list(ANDROID_TARGET_SDK_VERSION "android-target-sdk-version") + configure_file( "${CMAKE_BINARY_DIR}/android_deployment_settings.json.in" diff --git a/src/corelib/doc/src/dontdocument.qdoc b/src/corelib/doc/src/dontdocument.qdoc index b1af82fbe2..c84b789c46 100644 --- a/src/corelib/doc/src/dontdocument.qdoc +++ b/src/corelib/doc/src/dontdocument.qdoc @@ -34,7 +34,7 @@ QMetaTypeId2 QObjectData QObjectUserData QMapNodeBase QMapNode QMapDataBase QMapData QHashData QHashNode QArrayDataPointer QTextStreamManipulator QContiguousCacheData QContiguousCacheTypedData QNoDebug QUrlTwoFlags - QCborValueRef qfloat16 QDeferredDeleteEvent QSpecialInteger QLittleEndianStorageType + QCborValueRef QDeferredDeleteEvent QSpecialInteger QLittleEndianStorageType QBigEndianStorageType QFactoryInterface QFutureWatcherBase QJsonValuePtr QJsonValueRefPtr QAbstractConcatenable QStringBuilderCommon QTextCodec::ConverterState QThreadStorageData QTextStreamManipulator) diff --git a/src/corelib/doc/src/resource-system.qdoc b/src/corelib/doc/src/resource-system.qdoc index f9ef317799..6ff10804f5 100644 --- a/src/corelib/doc/src/resource-system.qdoc +++ b/src/corelib/doc/src/resource-system.qdoc @@ -179,6 +179,17 @@ rcc -compress 2 -compress-algo zlib myresources.qrc \endcode + It is also possible to use \c threshold, \c compress, and \c compress-algo + as attributes in a .qrc \c file tag. + + \code + <qresource> + <file compress="1" compress-algo="zstd">data.txt</file> + </qresource> + \endcode + + The above will select the \c zstd algorithm with compression level 1. + \c rcc supports the following compression algorithms and compression levels: @@ -196,10 +207,10 @@ library to choose an implementation-defined default. \li \c{zlib}: use the \l{https://zlib.net}{zlib} library to compress - contents. Valid compression levels range from 1 to 9, with 1the least - compression (least CPU time) and 9 the most compression (most CPU time). - The special value 0 means "no compression" and should not be used. The - default is implementation-defined, but usually is level 6. + contents. Valid compression levels range from 1 to 9, with 1 applying + the least compression (least CPU time) and 9 the most compression (most + CPU time). The special value 0 means "no compression" and should not be + used. The default is implementation-defined, but usually is level 6. \li \c{none}: no compression. This is the same as the \c{-no-compress} option. diff --git a/src/corelib/global/qfloat16.cpp b/src/corelib/global/qfloat16.cpp index 3d82bbe95a..1f06b10313 100644 --- a/src/corelib/global/qfloat16.cpp +++ b/src/corelib/global/qfloat16.cpp @@ -45,18 +45,20 @@ QT_BEGIN_NAMESPACE /*! - \headerfile <QFloat16> - \title 16-bit Floating Point Support + \class qfloat16 + \keyword 16-bit Floating Point Support \ingroup funclists - \brief The <QFloat16> header file provides 16-bit floating point support. - - This header file provides support for half-precision (16-bit) floating - point data with the class \c qfloat16. It is fully compliant with IEEE - 754 as a storage type. This implies that any arithmetic operation on a - \c qfloat16 instance results in the value first being converted to a - \c float. This conversion to and from \c float is performed by hardware - when possible, but on processors that do not natively support half-precision, - the conversion is performed through a sequence of lookup table operations. + \inmodule QtCore + \inheaderfile QFloat16 + \brief Provides 16-bit floating point support. + + The \c qfloat16 class provides support for half-precision (16-bit) floating + point data. It is fully compliant with IEEE 754 as a storage type. This + implies that any arithmetic operation on a \c qfloat16 instance results in + the value first being converted to a \c float. This conversion to and from + \c float is performed by hardware when possible, but on processors that do + not natively support half-precision, the conversion is performed through a + sequence of lookup table operations. \c qfloat16 should be treated as if it were a POD (plain old data) type. Consequently, none of the supported operations need any elaboration beyond @@ -68,7 +70,7 @@ QT_BEGIN_NAMESPACE /*! \macro QT_NO_FLOAT16_OPERATORS - \relates <QFloat16> + \relates qfloat16 \since 5.12.4 Defining this macro disables the arithmetic operators for qfloat16. @@ -81,7 +83,7 @@ QT_BEGIN_NAMESPACE /*! \fn bool qIsInf(qfloat16 f) - \relates <QFloat16> + \relates qfloat16 Returns true if the \c qfloat16 \a {f} is equivalent to infinity. @@ -90,7 +92,7 @@ QT_BEGIN_NAMESPACE /*! \fn bool qIsNaN(qfloat16 f) - \relates <QFloat16> + \relates qfloat16 Returns true if the \c qfloat16 \a {f} is not a number (NaN). @@ -99,7 +101,7 @@ QT_BEGIN_NAMESPACE /*! \fn bool qIsFinite(qfloat16 f) - \relates <QFloat16> + \relates qfloat16 Returns true if the \c qfloat16 \a {f} is a finite number. @@ -130,7 +132,7 @@ QT_BEGIN_NAMESPACE \since 5.14 \fn bool qfloat16::isNormal() const noexcept - Tests whether this \c qfloat16 value is finite and in normal form. + Returns \c true if this \c qfloat16 value is finite and in normal form. \sa qFpClassify() */ @@ -167,7 +169,7 @@ int qfloat16::fpClassify() const noexcept } /*! \fn int qRound(qfloat16 value) - \relates <QFloat16> + \relates qfloat16 Rounds \a value to the nearest integer. @@ -175,7 +177,7 @@ int qfloat16::fpClassify() const noexcept */ /*! \fn qint64 qRound64(qfloat16 value) - \relates <QFloat16> + \relates qfloat16 Rounds \a value to the nearest 64-bit integer. @@ -183,7 +185,7 @@ int qfloat16::fpClassify() const noexcept */ /*! \fn bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) - \relates <QFloat16> + \relates qfloat16 Compares the floating point value \a p1 and \a p2 and returns \c true if they are considered equal, otherwise \c false. @@ -256,7 +258,7 @@ static void qFloatFromFloat16_fast(float *, const quint16 *, qsizetype) noexcept #endif /*! \since 5.11 - \relates <QFloat16> + \relates qfloat16 Converts \a len floats from \a in to qfloat16 and stores them in \a out. Both \a in and \a out must have \a len allocated entries. @@ -272,7 +274,7 @@ Q_CORE_EXPORT void qFloatToFloat16(qfloat16 *out, const float *in, qsizetype len /*! \since 5.11 - \relates <QFloat16> + \relates qfloat16 Converts \a len qfloat16 from \a in to floats and stores them in \a out. Both \a in and \a out must have \a len allocated entries. diff --git a/src/corelib/io/qloggingregistry.cpp b/src/corelib/io/qloggingregistry.cpp index e8eb18b4c1..cacebfbda6 100644 --- a/src/corelib/io/qloggingregistry.cpp +++ b/src/corelib/io/qloggingregistry.cpp @@ -75,7 +75,7 @@ QLoggingRule::QLoggingRule() : \internal Constructs a logging rule. */ -QLoggingRule::QLoggingRule(const QStringRef &pattern, bool enabled) : +QLoggingRule::QLoggingRule(QStringView pattern, bool enabled) : messageType(-1), enabled(enabled) { @@ -87,7 +87,7 @@ QLoggingRule::QLoggingRule(const QStringRef &pattern, bool enabled) : Return value 1 means filter passed, 0 means filter doesn't influence this category, -1 means category doesn't pass this filter. */ -int QLoggingRule::pass(const QString &cat, QtMsgType msgType) const +int QLoggingRule::pass(QLatin1String cat, QtMsgType msgType) const { // check message type if (messageType > -1 && messageType != msgType) @@ -113,7 +113,7 @@ int QLoggingRule::pass(const QString &cat, QtMsgType msgType) const return (enabled ? 1 : -1); } else if (flags == RightFilter) { // matches right - if (idx == (cat.count() - category.count())) + if (idx == (cat.size() - category.count())) return (enabled ? 1 : -1); } } @@ -129,26 +129,22 @@ int QLoggingRule::pass(const QString &cat, QtMsgType msgType) const *.io.warning RightFilter, QtWarningMsg *.core.* MidFilter */ -void QLoggingRule::parse(const QStringRef &pattern) +void QLoggingRule::parse(QStringView pattern) { - QStringRef p; + QStringView p; // strip trailing ".messagetype" if (pattern.endsWith(QLatin1String(".debug"))) { - p = QStringRef(pattern.string(), pattern.position(), - pattern.length() - 6); // strlen(".debug") + p = pattern.chopped(6); // strlen(".debug") messageType = QtDebugMsg; } else if (pattern.endsWith(QLatin1String(".info"))) { - p = QStringRef(pattern.string(), pattern.position(), - pattern.length() - 5); // strlen(".info") + p = pattern.chopped(5); // strlen(".info") messageType = QtInfoMsg; } else if (pattern.endsWith(QLatin1String(".warning"))) { - p = QStringRef(pattern.string(), pattern.position(), - pattern.length() - 8); // strlen(".warning") + p = pattern.chopped(8); // strlen(".warning") messageType = QtWarningMsg; } else if (pattern.endsWith(QLatin1String(".critical"))) { - p = QStringRef(pattern.string(), pattern.position(), - pattern.length() - 9); // strlen(".critical") + p = pattern.chopped(9); // strlen(".critical") messageType = QtCriticalMsg; } else { p = pattern; @@ -159,11 +155,11 @@ void QLoggingRule::parse(const QStringRef &pattern) } else { if (p.endsWith(QLatin1Char('*'))) { flags |= LeftFilter; - p = QStringRef(p.string(), p.position(), p.length() - 1); + p = p.chopped(1); } if (p.startsWith(QLatin1Char('*'))) { flags |= RightFilter; - p = QStringRef(p.string(), p.position() + 1, p.length() - 1); + p = p.mid(1); } if (p.contains(QLatin1Char('*'))) // '*' only supported at start/end flags = PatternFlags(); @@ -208,7 +204,7 @@ void QLoggingSettingsParser::setContent(QTextStream &stream) _rules.clear(); QString line; while (stream.readLineInto(&line)) - parseNextLine(QStringRef(&line)); + parseNextLine(qToStringViewIgnoringNull(line)); } /*! @@ -216,7 +212,7 @@ void QLoggingSettingsParser::setContent(QTextStream &stream) Parses one line of the configuation file */ -void QLoggingSettingsParser::parseNextLine(QStringRef line) +void QLoggingSettingsParser::parseNextLine(QStringView line) { // Remove whitespace at start and end of line: line = line.trimmed(); @@ -227,7 +223,7 @@ void QLoggingSettingsParser::parseNextLine(QStringRef line) if (line.startsWith(QLatin1Char('[')) && line.endsWith(QLatin1Char(']'))) { // new section - auto sectionName = line.mid(1, line.size() - 2).trimmed(); + auto sectionName = line.mid(1).chopped(1).trimmed(); m_inRulesSection = sectionName.compare(QLatin1String("rules"), Qt::CaseInsensitive) == 0; return; } @@ -240,9 +236,9 @@ void QLoggingSettingsParser::parseNextLine(QStringRef line) #if QT_CONFIG(settings) QString tmp; QSettingsPrivate::iniUnescapedKey(key.toUtf8(), 0, key.length(), tmp); - QStringRef pattern = QStringRef(&tmp, 0, tmp.length()); + QStringView pattern = qToStringViewIgnoringNull(tmp); #else - QStringRef pattern = key; + QStringView pattern = key; #endif const auto valueStr = line.mid(equalPos + 1).trimmed(); int value = -1; @@ -252,7 +248,7 @@ void QLoggingSettingsParser::parseNextLine(QStringRef line) value = 0; QLoggingRule rule(pattern, (value == 1)); if (rule.flags != 0 && (value != -1)) - _rules.append(rule); + _rules.append(std::move(rule)); else warnMsg("Ignoring malformed logging rule: '%s'", line.toUtf8().constData()); } else { @@ -460,7 +456,7 @@ void QLoggingRegistry::defaultCategoryFilter(QLoggingCategory *cat) debug = false; } - QString categoryName = QLatin1String(cat->categoryName()); + const auto categoryName = QLatin1String(cat->categoryName()); for (const auto &ruleSet : reg->ruleSets) { for (const auto &rule : ruleSet) { diff --git a/src/corelib/io/qloggingregistry_p.h b/src/corelib/io/qloggingregistry_p.h index 12a1f166b3..3ac429b147 100644 --- a/src/corelib/io/qloggingregistry_p.h +++ b/src/corelib/io/qloggingregistry_p.h @@ -67,8 +67,8 @@ class Q_AUTOTEST_EXPORT QLoggingRule { public: QLoggingRule(); - QLoggingRule(const QStringRef &pattern, bool enabled); - int pass(const QString &categoryName, QtMsgType type) const; + QLoggingRule(QStringView pattern, bool enabled); + int pass(QLatin1String categoryName, QtMsgType type) const; enum PatternFlag { FullText = 0x1, @@ -84,7 +84,7 @@ public: bool enabled; private: - void parse(const QStringRef &pattern); + void parse(QStringView pattern); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QLoggingRule::PatternFlags) @@ -101,7 +101,7 @@ public: QVector<QLoggingRule> rules() const { return _rules; } private: - void parseNextLine(QStringRef line); + void parseNextLine(QStringView line); private: bool m_inRulesSection = false; diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index 3b708d7727..1f4be24913 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -2475,12 +2475,11 @@ int QProcess::execute(const QString &program, const QStringList &arguments) */ int QProcess::execute(const QString &command) { - QProcess process; - process.setProcessChannelMode(ForwardedChannels); - process.start(command); - if (!process.waitForFinished(-1) || process.error() == FailedToStart) + QStringList args = splitCommand(command); + if (args.isEmpty()) return -2; - return process.exitStatus() == QProcess::NormalExit ? process.exitCode() : -1; + QString program = args.takeFirst(); + return execute(program, args); } /*! diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index e636712e57..9a2fd196a2 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -732,7 +732,7 @@ const uchar *QResource::data() const \note If the data was compressed, this function will decompress every time it is called. The result is not cached between calls. - \sa uncompressedData(), size(), isCompressed(), isFile() + \sa uncompressedSize(), size(), isCompressed(), isFile() */ QByteArray QResource::uncompressedData() const diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 4d9f3b01de..ea0016c5af 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -337,7 +337,7 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) \value QPolygon QPolygon \value QPolygonF QPolygonF \value QColor QColor - \value QColorSpace QColorSpace + \value QColorSpace QColorSpace (introduced in Qt 5.15) \value QSizeF QSizeF \value QRectF QRectF \value QLine QLine @@ -383,7 +383,7 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) \value QCborMap QCborMap \value QCborSimpleType QCborSimpleType \value QModelIndex QModelIndex - \value QPersistentModelIndex QPersistentModelIndex (since 5.5) + \value QPersistentModelIndex QPersistentModelIndex (introduced in Qt 5.5) \value QUuid QUuid \value QByteArrayList QByteArrayList diff --git a/src/corelib/mimetypes/mimetypes.pri b/src/corelib/mimetypes/mimetypes.pri index 8cbe7b69ae..288ccf063e 100644 --- a/src/corelib/mimetypes/mimetypes.pri +++ b/src/corelib/mimetypes/mimetypes.pri @@ -26,9 +26,12 @@ qtConfig(mimetype) { qtConfig(mimetype-database) { outpath = .rcc + android { + outpath = $$outpath/$${QT_ARCH} + } debug_and_release { - CONFIG(debug, debug|release): outpath = .rcc/debug - else: outpath = .rcc/release + CONFIG(debug, debug|release): outpath = $$outpath/debug + else: outpath = $$outpath/release } mimedb.depends = $$PWD/mime/generate.pl diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h index e3fec1e62c..d260a9d678 100644 --- a/src/corelib/text/qbytearray.h +++ b/src/corelib/text/qbytearray.h @@ -219,8 +219,8 @@ public: int count(const char *a) const; int count(const QByteArray &a) const; - inline int compare(const char *c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; - inline int compare(const QByteArray &a, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline int compare(const char *c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; + inline int compare(const QByteArray &a, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; Q_REQUIRED_RESULT QByteArray left(int len) const; Q_REQUIRED_RESULT QByteArray right(int len) const; @@ -555,12 +555,12 @@ inline bool QByteArray::contains(const QByteArray &a) const { return indexOf(a) != -1; } inline bool QByteArray::contains(char c) const { return indexOf(c) != -1; } -inline int QByteArray::compare(const char *c, Qt::CaseSensitivity cs) const +inline int QByteArray::compare(const char *c, Qt::CaseSensitivity cs) const noexcept { return cs == Qt::CaseSensitive ? qstrcmp(*this, c) : qstrnicmp(data(), size(), c, -1); } -inline int QByteArray::compare(const QByteArray &a, Qt::CaseSensitivity cs) const +inline int QByteArray::compare(const QByteArray &a, Qt::CaseSensitivity cs) const noexcept { return cs == Qt::CaseSensitive ? qstrcmp(*this, a) : qstrnicmp(data(), size(), a.data(), a.size()); diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 757f248e8a..745d3d8047 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -6154,6 +6154,16 @@ QString& QString::fill(QChar ch, int size) sensitivity setting \a cs. */ +/*! + \fn int QString::compare(QChar ch, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + + \since 5.14 + \overload compare() + + Performs a comparison of this with \a ch, using the case + sensitivity setting \a cs. +*/ + #if QT_STRINGVIEW_LEVEL < 2 /*! \overload compare() @@ -9576,6 +9586,23 @@ QString &QString::setRawData(const QChar *unicode, int size) */ /*! + \fn int QLatin1String::compare(QStringView str, Qt::CaseSensitivity cs) const + \fn int QLatin1String::compare(QLatin1String l1, Qt::CaseSensitivity cs) const + \fn int QLatin1String::compare(QChar ch) const + \fn int QLatin1String::compare(QChar ch, Qt::CaseSensitivity cs) const + \since 5.14 + + Returns an integer that compares to zero as this Latin-1 string compares to the + string-view \a str, Latin-1 string \a l1, or character \a ch, respectively. + + If \a cs is Qt::CaseSensitive (the default), the comparison is case sensitive; + otherwise the comparison is case-insensitive. + + \sa operator==(), operator<(), operator>() +*/ + + +/*! \fn bool QLatin1String::startsWith(QStringView str, Qt::CaseSensitivity cs) const \since 5.10 \fn bool QLatin1String::startsWith(QLatin1String l1, Qt::CaseSensitivity cs) const @@ -10943,6 +10970,19 @@ QStringRef QStringRef::appendTo(QString *string) const /*! \overload + \fn int QStringRef::compare(QChar ch, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + \since 5.14 + + Compares this string with \a ch and returns an + integer less than, equal to, or greater than zero if this string + is less than, equal to, or greater than \a ch, interpreted as a string of length one. + + If \a cs is Qt::CaseSensitive, the comparison is case sensitive; + otherwise the comparison is case insensitive. +*/ + +/*! + \overload \fn int QStringRef::compare(QLatin1String other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const \since 4.5 diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index f4d6fc24a5..d5101b7ff3 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -110,6 +110,15 @@ public: Q_REQUIRED_RESULT Q_DECL_CONSTEXPR QLatin1Char front() const { return at(0); } Q_REQUIRED_RESULT Q_DECL_CONSTEXPR QLatin1Char back() const { return at(size() - 1); } + Q_REQUIRED_RESULT int compare(QStringView other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return QtPrivate::compareStrings(*this, other, cs); } + Q_REQUIRED_RESULT int compare(QLatin1String other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return QtPrivate::compareStrings(*this, other, cs); } + Q_REQUIRED_RESULT Q_DECL_CONSTEXPR int compare(QChar c) const noexcept + { return isEmpty() || front() == c ? size() - 1 : uchar(m_data[0]) - c.unicode() ; } + Q_REQUIRED_RESULT int compare(QChar c, Qt::CaseSensitivity cs) const noexcept + { return QtPrivate::compareStrings(*this, QStringView(&c, 1), cs); } + Q_REQUIRED_RESULT bool startsWith(QStringView s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return QtPrivate::startsWith(*this, s, cs); } Q_REQUIRED_RESULT bool startsWith(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept @@ -232,6 +241,8 @@ Q_DECL_CONSTEXPR bool QtPrivate::isLatin1(QLatin1String) noexcept // // QStringView members that require QLatin1String: // +int QStringView::compare(QLatin1String s, Qt::CaseSensitivity cs) const noexcept +{ return QtPrivate::compareStrings(*this, s, cs); } bool QStringView::startsWith(QLatin1String s, Qt::CaseSensitivity cs) const noexcept { return QtPrivate::startsWith(*this, s, cs); } bool QStringView::endsWith(QLatin1String s, Qt::CaseSensitivity cs) const noexcept @@ -729,6 +740,8 @@ public: #endif int compare(QLatin1String other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; inline int compare(QStringView s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; + int compare(QChar ch, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return compare(QStringView{&ch, 1}, cs); } static inline int compare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept @@ -1615,6 +1628,8 @@ public: int compare(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; int compare(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; + int compare(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return QtPrivate::compareStrings(*this, QStringView(&c, 1), cs); } int compare(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; #if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) int compare(const QByteArray &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const diff --git a/src/corelib/text/qstringview.cpp b/src/corelib/text/qstringview.cpp index 08dade7e68..9df656dc59 100644 --- a/src/corelib/text/qstringview.cpp +++ b/src/corelib/text/qstringview.cpp @@ -692,15 +692,29 @@ QT_BEGIN_NAMESPACE */ /*! - \fn int QStringView::compare(QStringView other, Qt::CaseSensitivity cs) const + \fn int QStringView::compare(QStringView str, Qt::CaseSensitivity cs) const \since 5.12 - Compares this string-view with the \a other string-view and returns an - integer less than, equal to, or greater than zero if this string-view - is less than, equal to, or greater than the other string-view. + Returns an integer that compares to zero as this string-view compares to the + string-view \a str. - If \a cs is Qt::CaseSensitive, the comparison is case sensitive; - otherwise the comparison is case insensitive. + If \a cs is Qt::CaseSensitive (the default), the comparison is case sensitive; + otherwise the comparison is case-insensitive. + + \sa operator==(), operator<(), operator>() +*/ + +/*! + \fn int QStringView::compare(QLatin1String l1, Qt::CaseSensitivity cs) const + \fn int QStringView::compare(QChar ch) const + \fn int QStringView::compare(QChar ch, Qt::CaseSensitivity cs) const + \since 5.14 + + Returns an integer that compares to zero as this string-view compares to the + Latin-1 string \a l1, or character \a ch, respectively. + + If \a cs is Qt::CaseSensitive (the default), the comparison is case sensitive; + otherwise the comparison is case-insensitive. \sa operator==(), operator<(), operator>() */ diff --git a/src/corelib/text/qstringview.h b/src/corelib/text/qstringview.h index 06391ffef4..83418970a5 100644 --- a/src/corelib/text/qstringview.h +++ b/src/corelib/text/qstringview.h @@ -257,6 +257,11 @@ public: Q_REQUIRED_RESULT int compare(QStringView other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return QtPrivate::compareStrings(*this, other, cs); } + Q_REQUIRED_RESULT inline int compare(QLatin1String other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; + Q_REQUIRED_RESULT Q_DECL_CONSTEXPR int compare(QChar c) const noexcept + { return empty() || front() == c ? size() - 1 : *utf16() - c.unicode() ; } + Q_REQUIRED_RESULT int compare(QChar c, Qt::CaseSensitivity cs) const noexcept + { return QtPrivate::compareStrings(*this, QStringView(&c, 1), cs); } Q_REQUIRED_RESULT bool startsWith(QStringView s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return QtPrivate::startsWith(*this, s, cs); } diff --git a/src/gui/doc/snippets/code/src_gui_image_qpixmapcache.cpp b/src/gui/doc/snippets/code/src_gui_image_qpixmapcache.cpp index 3870237ac3..9043ee6361 100644 --- a/src/gui/doc/snippets/code/src_gui_image_qpixmapcache.cpp +++ b/src/gui/doc/snippets/code/src_gui_image_qpixmapcache.cpp @@ -49,9 +49,8 @@ ****************************************************************************/ //! [0] -QPixmap* pp; QPixmap p; -if ((pp=QPixmapCache::find("my_big_image", pm))) { +if (QPixmap *pp = QPixmapCache::find("my_big_image"))) { p = *pp; } else { p.load("bigimage.png"); diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index ce0c6170c1..189f12fd5c 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -68,6 +68,11 @@ #include <private/qimage_p.h> #include <private/qfont_p.h> +#if QT_CONFIG(thread) +#include "qsemaphore.h" +#include "qthreadpool.h" +#endif + QT_BEGIN_NAMESPACE static inline bool isLocked(QImageData *data) @@ -4861,18 +4866,43 @@ void QImage::applyColorTransform(const QColorTransform &transform) Q_UNREACHABLE(); } + std::function<void(int,int)> transformSegment; + if (depth() > 32) { - for (int i = 0; i < height(); ++i) { - QRgba64 *scanline = reinterpret_cast<QRgba64 *>(scanLine(i)); - transform.d->apply(scanline, scanline, width(), flags); - } + transformSegment = [&](int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + QRgba64 *scanline = reinterpret_cast<QRgba64 *>(scanLine(y)); + transform.d->apply(scanline, scanline, width(), flags); + } + }; } else { - for (int i = 0; i < height(); ++i) { - QRgb *scanline = reinterpret_cast<QRgb *>(scanLine(i)); - transform.d->apply(scanline, scanline, width(), flags); - } + transformSegment = [&](int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + QRgb *scanline = reinterpret_cast<QRgb *>(scanLine(y)); + transform.d->apply(scanline, scanline, width(), flags); + } + }; } +#if QT_CONFIG(thread) + int segments = sizeInBytes() / (1<<16); + segments = std::min(segments, height()); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (height() - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + transformSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + } else +#endif + transformSegment(0, height()); + if (oldFormat != format()) *this = std::move(*this).convertToFormat(oldFormat); } diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 27088698ec..4f570d8684 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -43,7 +43,12 @@ #include <private/qendian_p.h> #include <private/qsimd_p.h> #include <private/qimage_p.h> + #include <qendian.h> +#if QT_CONFIG(thread) +#include <qsemaphore.h> +#include <qthreadpool.h> +#endif QT_BEGIN_NAMESPACE @@ -159,12 +164,8 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio // Cannot be used with indexed formats. Q_ASSERT(dest->format > QImage::Format_Indexed8); Q_ASSERT(src->format > QImage::Format_Indexed8); - uint buf[BufferSize]; - uint *buffer = buf; const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; - const uchar *srcData = src->data; - uchar *destData = dest->data; FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM; ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM; @@ -197,59 +198,110 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio else store = destLayout->storeFromRGB32; } - QDitherInfo dither; - QDitherInfo *ditherPtr = nullptr; - if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) - ditherPtr = &dither; - - for (int y = 0; y < src->height; ++y) { - dither.y = y; - int x = 0; - while (x < src->width) { - dither.x = x; - int l = src->width - x; - if (destLayout->bpp == QPixelLayout::BPP32) - buffer = reinterpret_cast<uint *>(destData) + x; - else - l = qMin(l, BufferSize); - const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr); - store(destData, ptr, x, l, nullptr, ditherPtr); - x += l; + + auto convertSegment = [=](int yStart, int yEnd) { + uint buf[BufferSize]; + uint *buffer = buf; + const uchar *srcData = src->data + src->bytes_per_line * yStart; + uchar *destData = dest->data + dest->bytes_per_line * yStart; + QDitherInfo dither; + QDitherInfo *ditherPtr = nullptr; + if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) + ditherPtr = &dither; + for (int y = yStart; y < yEnd; ++y) { + dither.y = y; + int x = 0; + while (x < src->width) { + dither.x = x; + int l = src->width - x; + if (destLayout->bpp == QPixelLayout::BPP32) + buffer = reinterpret_cast<uint *>(destData) + x; + else + l = qMin(l, BufferSize); + const uint *ptr = fetch(buffer, srcData, x, l, 0, ditherPtr); + store(destData, ptr, x, l, 0, ditherPtr); + x += l; + } + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; } - srcData += src->bytes_per_line; - destData += dest->bytes_per_line; - } + }; + +#if QT_CONFIG(thread) + int segments = src->nbytes / (1<<16); + segments = std::min(segments, src->height); + + if (segments <= 1) + return convertSegment(0, src->height); + + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (src->height - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); +#else + convertSegment(0, src->height); +#endif } void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(dest->format > QImage::Format_Indexed8); Q_ASSERT(src->format > QImage::Format_Indexed8); - QRgba64 buf[BufferSize]; - QRgba64 *buffer = buf; const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; - const uchar *srcData = src->data; - uchar *destData = dest->data; const FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM; const ConvertAndStorePixelsFunc64 store = qStoreFromRGBA64PM[dest->format]; - for (int y = 0; y < src->height; ++y) { - int x = 0; - while (x < src->width) { - int l = src->width - x; - if (destLayout->bpp == QPixelLayout::BPP64) - buffer = reinterpret_cast<QRgba64 *>(destData) + x; - else - l = qMin(l, BufferSize); - const QRgba64 *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr); - store(destData, ptr, x, l, nullptr, nullptr); - x += l; + auto convertSegment = [=](int yStart, int yEnd) { + QRgba64 buf[BufferSize]; + QRgba64 *buffer = buf; + const uchar *srcData = src->data + yStart * src->bytes_per_line; + uchar *destData = dest->data + yStart * dest->bytes_per_line; + for (int y = yStart; y < yEnd; ++y) { + int x = 0; + while (x < src->width) { + int l = src->width - x; + if (destLayout->bpp == QPixelLayout::BPP64) + buffer = reinterpret_cast<QRgba64 *>(destData) + x; + else + l = qMin(l, BufferSize); + const QRgba64 *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr); + store(destData, ptr, x, l, nullptr, nullptr); + x += l; + } + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; } - srcData += src->bytes_per_line; - destData += dest->bytes_per_line; - } + }; +#if QT_CONFIG(thread) + int segments = src->nbytes / (1<<16); + segments = std::min(segments, src->height); + + if (segments <= 1) + return convertSegment(0, src->height); + + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (src->height - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); +#else + convertSegment(0, src->height); +#endif } bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags flags) @@ -270,11 +322,6 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im && qt_highColorPrecision(dst_format, !srcLayout->hasAlphaChannel)) return false; - uint buf[BufferSize]; - uint *buffer = buf; - uchar *srcData = data->data; - uchar *destData = data->data; - QImageData::ImageSizeParameters params = { data->bytes_per_line, data->nbytes }; if (data->depth != destDepth) { params = QImageData::calculateImageParameters(data->width, data->height, destDepth); @@ -313,28 +360,52 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im else store = destLayout->storeFromRGB32; } - QDitherInfo dither; - QDitherInfo *ditherPtr = nullptr; - if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) - ditherPtr = &dither; - - for (int y = 0; y < data->height; ++y) { - dither.y = y; - int x = 0; - while (x < data->width) { - dither.x = x; - int l = data->width - x; - if (srcLayout->bpp == QPixelLayout::BPP32) - buffer = reinterpret_cast<uint *>(srcData) + x; - else - l = qMin(l, BufferSize); - const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr); - store(destData, ptr, x, l, nullptr, ditherPtr); - x += l; + + auto convertSegment = [=](int yStart, int yEnd) { + uint buf[BufferSize]; + uint *buffer = buf; + uchar *srcData = data->data + data->bytes_per_line * yStart; + uchar *destData = srcData; + QDitherInfo dither; + QDitherInfo *ditherPtr = nullptr; + if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) + ditherPtr = &dither; + for (int y = yStart; y < yEnd; ++y) { + dither.y = y; + int x = 0; + while (x < data->width) { + dither.x = x; + int l = data->width - x; + if (srcLayout->bpp == QPixelLayout::BPP32) + buffer = reinterpret_cast<uint *>(srcData) + x; + else + l = qMin(l, BufferSize); + const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr); + store(destData, ptr, x, l, nullptr, ditherPtr); + x += l; + } + srcData += data->bytes_per_line; + destData += params.bytesPerLine; } - srcData += data->bytes_per_line; - destData += params.bytesPerLine; - } + }; +#if QT_CONFIG(thread) + int segments = data->nbytes / (1<<16); + segments = std::min(segments, data->height); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (data->height - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + } else +#endif + convertSegment(0, data->height); if (params.totalSize != data->nbytes) { Q_ASSERT(params.totalSize < data->nbytes); void *newData = realloc(data->data, params.totalSize); diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp index 2e2f65b483..ecb0230e71 100644 --- a/src/gui/painting/qimagescale.cpp +++ b/src/gui/painting/qimagescale.cpp @@ -43,6 +43,11 @@ #include "qcolor.h" #include "qrgba64_p.h" +#if QT_CONFIG(thread) +#include "qsemaphore.h" +#include "qthreadpool.h" +#endif + QT_BEGIN_NAMESPACE /* @@ -239,6 +244,8 @@ static QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img, isi = new QImageScaleInfo; if (!isi) return nullptr; + isi->sh = sh; + isi->sw = sw; isi->xup_yup = (qAbs(dw) >= sw) + ((qAbs(dh) >= sh) << 1); @@ -303,33 +310,54 @@ static void qt_qimageScaleAARGBA_up_xy(QImageScaleInfo *isi, unsigned int *dest, int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - /* calculate the source line we'll scan from */ - const unsigned int *sptr = ypoints[y]; - unsigned int *dptr = dest + (y * dow); - const int yap = yapoints[y]; - if (yap > 0) { - for (int x = 0; x < dw; x++) { - const unsigned int *pix = sptr + xpoints[x]; - const int xap = xapoints[x]; - if (xap > 0) - *dptr = interpolate_4_pixels(pix, pix + sow, xap, yap); - else - *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - yap, pix[sow], yap); - dptr++; - } - } else { - for (int x = 0; x < dw; x++) { - const unsigned int *pix = sptr + xpoints[x]; - const int xap = xapoints[x]; - if (xap > 0) - *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - xap, pix[1], xap); - else - *dptr = pix[0]; - dptr++; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + /* calculate the source line we'll scan from */ + const unsigned int *sptr = ypoints[y]; + unsigned int *dptr = dest + (y * dow); + const int yap = yapoints[y]; + if (yap > 0) { + for (int x = 0; x < dw; x++) { + const unsigned int *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = interpolate_4_pixels(pix, pix + sow, xap, yap); + else + *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - yap, pix[sow], yap); + dptr++; + } + } else { + for (int x = 0; x < dw; x++) { + const unsigned int *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - xap, pix[1], xap); + else + *dptr = pix[0]; + dptr++; + } } } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } /* scale by area sampling - with alpha */ @@ -411,33 +439,54 @@ static void qt_qimageScaleAARGBA_up_x_down_y(QImageScaleInfo *isi, unsigned int int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int r, g, b, a; - qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, r, g, b, a); - - int xap = xapoints[x]; - if (xap > 0) { - int rr, gg, bb, aa; - qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); - - r = r * (256 - xap); - g = g * (256 - xap); - b = b * (256 - xap); - a = a * (256 - xap); - r = (r + (rr * xap)) >> 8; - g = (g + (gg * xap)) >> 8; - b = (b + (bb * xap)) >> 8; - a = (a + (aa * xap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; + + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int r, g, b, a; + qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, r, g, b, a); + + int xap = xapoints[x]; + if (xap > 0) { + int rr, gg, bb, aa; + qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); + + r = r * (256 - xap); + g = g * (256 - xap); + b = b * (256 - xap); + a = a * (256 - xap); + r = (r + (rr * xap)) >> 8; + g = (g + (gg * xap)) >> 8; + b = (b + (bb * xap)) >> 8; + a = (a + (aa * xap)) >> 8; + } + *dptr++ = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); } - *dptr++ = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest, @@ -449,34 +498,55 @@ static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int r, g, b, a; - qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, r, g, b, a); - - int yap = yapoints[y]; - if (yap > 0) { - int rr, gg, bb, aa; - qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); - - r = r * (256 - yap); - g = g * (256 - yap); - b = b * (256 - yap); - a = a * (256 - yap); - r = (r + (rr * yap)) >> 8; - g = (g + (gg * yap)) >> 8; - b = (b + (bb * yap)) >> 8; - a = (a + (aa * yap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int r, g, b, a; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, r, g, b, a); + + int yap = yapoints[y]; + if (yap > 0) { + int rr, gg, bb, aa; + qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); + + r = r * (256 - yap); + g = g * (256 - yap); + b = b * (256 - yap); + a = a * (256 - yap); + r = (r + (rr * yap)) >> 8; + g = (g + (gg * yap)) >> 8; + b = (b + (bb * yap)) >> 8; + a = (a + (aa * yap)) >> 8; + } + *dptr = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); + dptr++; } - *dptr = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *dest, @@ -487,45 +557,66 @@ static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *des int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = (yapoints[y]) >> 16; - int yap = (yapoints[y]) & 0xffff; - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = (yapoints[y]) >> 16; + int yap = (yapoints[y]) & 0xffff; - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int rx, gx, bx, ax; - qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; - int r = ((rx>>4) * yap); - int g = ((gx>>4) * yap); - int b = ((bx>>4) * yap); - int a = ((ax>>4) * yap); + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int rx, gx, bx, ax; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + int r = ((rx>>4) * yap); + int g = ((gx>>4) * yap); + int b = ((bx>>4) * yap); + int a = ((ax>>4) * yap); + + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + r += ((rx>>4) * Cy); + g += ((gx>>4) * Cy); + b += ((bx>>4) * Cy); + a += ((ax>>4) * Cy); + } sptr += sow; qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - r += ((rx>>4) * Cy); - g += ((gx>>4) * Cy); - b += ((bx>>4) * Cy); - a += ((ax>>4) * Cy); - } - sptr += sow; - qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - r += ((rx>>4) * j); - g += ((gx>>4) * j); - b += ((bx>>4) * j); - a += ((ax>>4) * j); + r += ((rx>>4) * j); + g += ((gx>>4) * j); + b += ((bx>>4) * j); + a += ((ax>>4) * j); - *dptr = qRgba(r >> 24, g >> 24, b >> 24, a >> 24); - dptr++; + *dptr = qRgba(r >> 24, g >> 24, b >> 24, a >> 24); + dptr++; + } } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } #if QT_CONFIG(raster_64bit) @@ -546,32 +637,53 @@ static void qt_qimageScaleRgba64_up_xy(QImageScaleInfo *isi, QRgba64 *dest, int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - const QRgba64 *sptr = ypoints[y]; - QRgba64 *dptr = dest + (y * dow); - const int yap = yapoints[y]; - if (yap > 0) { - for (int x = 0; x < dw; x++) { - const QRgba64 *pix = sptr + xpoints[x]; - const int xap = xapoints[x]; - if (xap > 0) - *dptr = interpolate_4_pixels_rgb64(pix, pix + sow, xap * 256, yap * 256); - else - *dptr = interpolate256(pix[0], 256 - yap, pix[sow], yap); - dptr++; - } - } else { - for (int x = 0; x < dw; x++) { - const QRgba64 *pix = sptr + xpoints[x]; - const int xap = xapoints[x]; - if (xap > 0) - *dptr = interpolate256(pix[0], 256 - xap, pix[1], xap); - else - *dptr = pix[0]; - dptr++; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + const QRgba64 *sptr = ypoints[y]; + QRgba64 *dptr = dest + (y * dow); + const int yap = yapoints[y]; + if (yap > 0) { + for (int x = 0; x < dw; x++) { + const QRgba64 *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = interpolate_4_pixels_rgb64(pix, pix + sow, xap * 256, yap * 256); + else + *dptr = interpolate256(pix[0], 256 - yap, pix[sow], yap); + dptr++; + } + } else { + for (int x = 0; x < dw; x++) { + const QRgba64 *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = interpolate256(pix[0], 256 - xap, pix[1], xap); + else + *dptr = pix[0]; + dptr++; + } } } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } void qt_qimageScaleRgba64(QImageScaleInfo *isi, QRgba64 *dest, @@ -616,33 +728,54 @@ static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = (yapoints[y]) >> 16; - int yap = (yapoints[y]) & 0xffff; - - QRgba64 *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const QRgba64 *sptr = ypoints[y] + xpoints[x]; - qint64 r, g, b, a; - qt_qimageScaleRgba64_helper(sptr, yap, Cy, sow, r, g, b, a); - - int xap = xapoints[x]; - if (xap > 0) { - qint64 rr, gg, bb, aa; - qt_qimageScaleRgba64_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); - - r = r * (256 - xap); - g = g * (256 - xap); - b = b * (256 - xap); - a = a * (256 - xap); - r = (r + (rr * xap)) >> 8; - g = (g + (gg * xap)) >> 8; - b = (b + (bb * xap)) >> 8; - a = (a + (aa * xap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = (yapoints[y]) >> 16; + int yap = (yapoints[y]) & 0xffff; + + QRgba64 *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const QRgba64 *sptr = ypoints[y] + xpoints[x]; + qint64 r, g, b, a; + qt_qimageScaleRgba64_helper(sptr, yap, Cy, sow, r, g, b, a); + + int xap = xapoints[x]; + if (xap > 0) { + qint64 rr, gg, bb, aa; + qt_qimageScaleRgba64_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); + + r = r * (256 - xap); + g = g * (256 - xap); + b = b * (256 - xap); + a = a * (256 - xap); + r = (r + (rr * xap)) >> 8; + g = (g + (gg * xap)) >> 8; + b = (b + (bb * xap)) >> 8; + a = (a + (aa * xap)) >> 8; + } + *dptr++ = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14); } - *dptr++ = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14); } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest, @@ -653,34 +786,55 @@ static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - QRgba64 *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const QRgba64 *sptr = ypoints[y] + xpoints[x]; - qint64 r, g, b, a; - qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, r, g, b, a); - - int yap = yapoints[y]; - if (yap > 0) { - qint64 rr, gg, bb, aa; - qt_qimageScaleRgba64_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); - - r = r * (256 - yap); - g = g * (256 - yap); - b = b * (256 - yap); - a = a * (256 - yap); - r = (r + (rr * yap)) >> 8; - g = (g + (gg * yap)) >> 8; - b = (b + (bb * yap)) >> 8; - a = (a + (aa * yap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + QRgba64 *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const QRgba64 *sptr = ypoints[y] + xpoints[x]; + qint64 r, g, b, a; + qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, r, g, b, a); + + int yap = yapoints[y]; + if (yap > 0) { + qint64 rr, gg, bb, aa; + qt_qimageScaleRgba64_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); + + r = r * (256 - yap); + g = g * (256 - yap); + b = b * (256 - yap); + a = a * (256 - yap); + r = (r + (rr * yap)) >> 8; + g = (g + (gg * yap)) >> 8; + b = (b + (bb * yap)) >> 8; + a = (a + (aa * yap)) >> 8; + } + *dptr = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14); + dptr++; } - *dptr = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14); - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest, @@ -691,43 +845,64 @@ static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest, int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = (yapoints[y]) >> 16; - int yap = (yapoints[y]) & 0xffff; - - QRgba64 *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const QRgba64 *sptr = ypoints[y] + xpoints[x]; - qint64 rx, gx, bx, ax; - qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - - qint64 r = rx * yap; - qint64 g = gx * yap; - qint64 b = bx * yap; - qint64 a = ax * yap; - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = (yapoints[y]) >> 16; + int yap = (yapoints[y]) & 0xffff; + + QRgba64 *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const QRgba64 *sptr = ypoints[y] + xpoints[x]; + qint64 rx, gx, bx, ax; + qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + + qint64 r = rx * yap; + qint64 g = gx * yap; + qint64 b = bx * yap; + qint64 a = ax * yap; + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + r += rx * Cy; + g += gx * Cy; + b += bx * Cy; + a += ax * Cy; + } sptr += sow; qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - r += rx * Cy; - g += gx * Cy; - b += bx * Cy; - a += ax * Cy; + r += rx * j; + g += gx * j; + b += bx * j; + a += ax * j; + + *dptr = qRgba64(r >> 28, g >> 28, b >> 28, a >> 28); + dptr++; } - sptr += sow; - qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - r += rx * j; - g += gx * j; - b += bx * j; - a += ax * j; - - *dptr = qRgba64(r >> 28, g >> 28, b >> 28, a >> 28); - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } #endif @@ -817,31 +992,52 @@ static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int * int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int r, g, b; - qt_qimageScaleAARGB_helper(sptr, yap, Cy, sow, r, g, b); - - int xap = xapoints[x]; - if (xap > 0) { - int rr, bb, gg; - qt_qimageScaleAARGB_helper(sptr + 1, yap, Cy, sow, rr, gg, bb); - - r = r * (256 - xap); - g = g * (256 - xap); - b = b * (256 - xap); - r = (r + (rr * xap)) >> 8; - g = (g + (gg * xap)) >> 8; - b = (b + (bb * xap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; + + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int r, g, b; + qt_qimageScaleAARGB_helper(sptr, yap, Cy, sow, r, g, b); + + int xap = xapoints[x]; + if (xap > 0) { + int rr, bb, gg; + qt_qimageScaleAARGB_helper(sptr + 1, yap, Cy, sow, rr, gg, bb); + + r = r * (256 - xap); + g = g * (256 - xap); + b = b * (256 - xap); + r = (r + (rr * xap)) >> 8; + g = (g + (gg * xap)) >> 8; + b = (b + (bb * xap)) >> 8; + } + *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); } - *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest, @@ -853,31 +1049,52 @@ static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int * int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int r, g, b; - qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, r, g, b); - - int yap = yapoints[y]; - if (yap > 0) { - int rr, bb, gg; - qt_qimageScaleAARGB_helper(sptr + sow, xap, Cx, 1, rr, gg, bb); - - r = r * (256 - yap); - g = g * (256 - yap); - b = b * (256 - yap); - r = (r + (rr * yap)) >> 8; - g = (g + (gg * yap)) >> 8; - b = (b + (bb * yap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int r, g, b; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, r, g, b); + + int yap = yapoints[y]; + if (yap > 0) { + int rr, bb, gg; + qt_qimageScaleAARGB_helper(sptr + sow, xap, Cx, 1, rr, gg, bb); + + r = r * (256 - yap); + g = g * (256 - yap); + b = b * (256 - yap); + r = (r + (rr * yap)) >> 8; + g = (g + (gg * yap)) >> 8; + b = (b + (bb * yap)) >> 8; + } + *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); } - *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleAARGB_down_xy(QImageScaleInfo *isi, unsigned int *dest, @@ -888,43 +1105,64 @@ static void qt_qimageScaleAARGB_down_xy(QImageScaleInfo *isi, unsigned int *dest int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int rx, gx, bx; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int rx, gx, bx; - qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); + int r = (rx >> 4) * yap; + int g = (gx >> 4) * yap; + int b = (bx >> 4) * yap; - int r = (rx >> 4) * yap; - int g = (gx >> 4) * yap; - int b = (bx >> 4) * yap; + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + r += (rx >> 4) * Cy; + g += (gx >> 4) * Cy; + b += (bx >> 4) * Cy; + } sptr += sow; qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); - r += (rx >> 4) * Cy; - g += (gx >> 4) * Cy; - b += (bx >> 4) * Cy; - } - sptr += sow; - qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); - - r += (rx >> 4) * j; - g += (gx >> 4) * j; - b += (bx >> 4) * j; + r += (rx >> 4) * j; + g += (gx >> 4) * j; + b += (bx >> 4) * j; - *dptr = qRgb(r >> 24, g >> 24, b >> 24); - dptr++; + *dptr = qRgb(r >> 24, g >> 24, b >> 24); + dptr++; + } } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } QImage qSmoothScaleImage(const QImage &src, int dw, int dh) diff --git a/src/gui/painting/qimagescale_neon.cpp b/src/gui/painting/qimagescale_neon.cpp index 4ae113b002..416155e139 100644 --- a/src/gui/painting/qimagescale_neon.cpp +++ b/src/gui/painting/qimagescale_neon.cpp @@ -41,6 +41,11 @@ #include "qimage.h" #include <private/qsimd_p.h> +#if QT_CONFIG(thread) +#include "qsemaphore.h" +#include "qthreadpool.h" +#endif + #if defined(__ARM_NEON__) QT_BEGIN_NAMESPACE @@ -76,33 +81,54 @@ void qt_qimageScaleAARGBA_up_x_down_y_neon(QImageScaleInfo *isi, unsigned int *d int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const unsigned int *sptr = ypoints[y] + xpoints[x]; - uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow); - - int xap = xapoints[x]; - if (xap > 0) { - uint32x4_t vr = qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow); - - vx = vmulq_n_u32(vx, 256 - xap); - vr = vmulq_n_u32(vr, xap); - vx = vaddq_u32(vx, vr); - vx = vshrq_n_u32(vx, 8); + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; + + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const unsigned int *sptr = ypoints[y] + xpoints[x]; + uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow); + + int xap = xapoints[x]; + if (xap > 0) { + uint32x4_t vr = qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow); + + vx = vmulq_n_u32(vx, 256 - xap); + vr = vmulq_n_u32(vr, xap); + vx = vaddq_u32(vx, vr); + vx = vshrq_n_u32(vx, 8); + } + vx = vshrq_n_u32(vx, 14); + const uint16x4_t vx16 = vmovn_u32(vx); + const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); + *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - vx = vshrq_n_u32(vx, 14); - const uint16x4_t vx16 = vmovn_u32(vx); - const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); - *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template<bool RGB> @@ -115,33 +141,54 @@ void qt_qimageScaleAARGBA_down_x_up_y_neon(QImageScaleInfo *isi, unsigned int *d int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); - - int yap = yapoints[y]; - if (yap > 0) { - uint32x4_t vr = qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1); - - vx = vmulq_n_u32(vx, 256 - yap); - vr = vmulq_n_u32(vr, yap); - vx = vaddq_u32(vx, vr); - vx = vshrq_n_u32(vx, 8); + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); + + int yap = yapoints[y]; + if (yap > 0) { + uint32x4_t vr = qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1); + + vx = vmulq_n_u32(vx, 256 - yap); + vr = vmulq_n_u32(vr, yap); + vx = vaddq_u32(vx, vr); + vx = vshrq_n_u32(vx, 8); + } + vx = vshrq_n_u32(vx, 14); + const uint16x4_t vx16 = vmovn_u32(vx); + const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); + *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - vx = vshrq_n_u32(vx, 14); - const uint16x4_t vx16 = vmovn_u32(vx); - const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); - *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template<bool RGB> @@ -153,43 +200,64 @@ void qt_qimageScaleAARGBA_down_xy_neon(QImageScaleInfo *isi, unsigned int *dest, int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const int Cx = xapoints[x] >> 16; - const int xap = xapoints[x] & 0xffff; + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const int Cx = xapoints[x] >> 16; + const int xap = xapoints[x] & 0xffff; - const unsigned int *sptr = ypoints[y] + xpoints[x]; - uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); - vx = vshrq_n_u32(vx, 4); - uint32x4_t vr = vmulq_n_u32(vx, yap); + const unsigned int *sptr = ypoints[y] + xpoints[x]; + uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); + vx = vshrq_n_u32(vx, 4); + uint32x4_t vr = vmulq_n_u32(vx, yap); - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); + vx = vshrq_n_u32(vx, 4); + vx = vmulq_n_u32(vx, Cy); + vr = vaddq_u32(vr, vx); + } sptr += sow; vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); vx = vshrq_n_u32(vx, 4); - vx = vmulq_n_u32(vx, Cy); + vx = vmulq_n_u32(vx, j); vr = vaddq_u32(vr, vx); + + vx = vshrq_n_u32(vr, 24); + const uint16x4_t vx16 = vmovn_u32(vx); + const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); + *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - sptr += sow; - vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); - vx = vshrq_n_u32(vx, 4); - vx = vmulq_n_u32(vx, j); - vr = vaddq_u32(vr, vx); - - vx = vshrq_n_u32(vr, 24); - const uint16x4_t vx16 = vmovn_u32(vx); - const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); - *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template void qt_qimageScaleAARGBA_up_x_down_y_neon<false>(QImageScaleInfo *isi, unsigned int *dest, diff --git a/src/gui/painting/qimagescale_p.h b/src/gui/painting/qimagescale_p.h index 244d681718..a9a4c0f858 100644 --- a/src/gui/painting/qimagescale_p.h +++ b/src/gui/painting/qimagescale_p.h @@ -66,6 +66,8 @@ namespace QImageScale { int *xapoints{nullptr}; int *yapoints{nullptr}; int xup_yup{0}; + int sh = 0; + int sw = 0; }; } diff --git a/src/gui/painting/qimagescale_sse4.cpp b/src/gui/painting/qimagescale_sse4.cpp index 5861a2e2ff..902ae61ed2 100644 --- a/src/gui/painting/qimagescale_sse4.cpp +++ b/src/gui/painting/qimagescale_sse4.cpp @@ -42,6 +42,11 @@ #include <private/qdrawhelper_x86_p.h> #include <private/qsimd_p.h> +#if QT_CONFIG(thread) +#include "qsemaphore.h" +#include "qthreadpool.h" +#endif + #if defined(QT_COMPILER_SUPPORTS_SSE4_1) QT_BEGIN_NAMESPACE @@ -70,44 +75,65 @@ void qt_qimageScaleAARGBA_up_x_down_y_sse4(QImageScaleInfo *isi, unsigned int *d int dw, int dh, int dow, int sow) { const unsigned int **ypoints = isi->ypoints; - int *xpoints = isi->xpoints; - int *xapoints = isi->xapoints; - int *yapoints = isi->yapoints; + const int *xpoints = isi->xpoints; + const int *xapoints = isi->xapoints; + const int *yapoints = isi->yapoints; const __m128i v256 = _mm_set1_epi32(256); /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - const __m128i vCy = _mm_set1_epi32(Cy); - const __m128i vyap = _mm_set1_epi32(yap); - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const unsigned int *sptr = ypoints[y] + xpoints[x]; - __m128i vx = qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, vyap, vCy); - - int xap = xapoints[x]; - if (xap > 0) { - const __m128i vxap = _mm_set1_epi32(xap); - const __m128i vinvxap = _mm_sub_epi32(v256, vxap); - __m128i vr = qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, vyap, vCy); + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + const int Cy = yapoints[y] >> 16; + const int yap = yapoints[y] & 0xffff; + const __m128i vCy = _mm_set1_epi32(Cy); + const __m128i vyap = _mm_set1_epi32(yap); - vx = _mm_mullo_epi32(vx, vinvxap); - vr = _mm_mullo_epi32(vr, vxap); - vx = _mm_add_epi32(vx, vr); - vx = _mm_srli_epi32(vx, 8); + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const unsigned int *sptr = ypoints[y] + xpoints[x]; + __m128i vx = qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, vyap, vCy); + + const int xap = xapoints[x]; + if (xap > 0) { + const __m128i vxap = _mm_set1_epi32(xap); + const __m128i vinvxap = _mm_sub_epi32(v256, vxap); + __m128i vr = qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, vyap, vCy); + + vx = _mm_mullo_epi32(vx, vinvxap); + vr = _mm_mullo_epi32(vr, vxap); + vx = _mm_add_epi32(vx, vr); + vx = _mm_srli_epi32(vx, 8); + } + vx = _mm_srli_epi32(vx, 14); + vx = _mm_packus_epi32(vx, vx); + vx = _mm_packus_epi16(vx, vx); + *dptr = _mm_cvtsi128_si32(vx); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - vx = _mm_srli_epi32(vx, 14); - vx = _mm_packus_epi32(vx, _mm_setzero_si128()); - vx = _mm_packus_epi16(vx, _mm_setzero_si128()); - *dptr = _mm_cvtsi128_si32(vx); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template<bool RGB> @@ -122,37 +148,58 @@ void qt_qimageScaleAARGBA_down_x_up_y_sse4(QImageScaleInfo *isi, unsigned int *d const __m128i v256 = _mm_set1_epi32(256); /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - const __m128i vCx = _mm_set1_epi32(Cx); - const __m128i vxap = _mm_set1_epi32(xap); - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - __m128i vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); - - int yap = yapoints[y]; - if (yap > 0) { - const __m128i vyap = _mm_set1_epi32(yap); - const __m128i vinvyap = _mm_sub_epi32(v256, vyap); - __m128i vr = qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, vxap, vCx); - - vx = _mm_mullo_epi32(vx, vinvyap); - vr = _mm_mullo_epi32(vr, vyap); - vx = _mm_add_epi32(vx, vr); - vx = _mm_srli_epi32(vx, 8); + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + const __m128i vCx = _mm_set1_epi32(Cx); + const __m128i vxap = _mm_set1_epi32(xap); + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + __m128i vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); + + int yap = yapoints[y]; + if (yap > 0) { + const __m128i vyap = _mm_set1_epi32(yap); + const __m128i vinvyap = _mm_sub_epi32(v256, vyap); + __m128i vr = qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, vxap, vCx); + + vx = _mm_mullo_epi32(vx, vinvyap); + vr = _mm_mullo_epi32(vr, vyap); + vx = _mm_add_epi32(vx, vr); + vx = _mm_srli_epi32(vx, 8); + } + vx = _mm_srli_epi32(vx, 14); + vx = _mm_packus_epi32(vx, vx); + vx = _mm_packus_epi16(vx, vx); + *dptr = _mm_cvtsi128_si32(vx); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - vx = _mm_srli_epi32(vx, 14); - vx = _mm_packus_epi32(vx, _mm_setzero_si128()); - vx = _mm_packus_epi16(vx, _mm_setzero_si128()); - *dptr = _mm_cvtsi128_si32(vx); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template<bool RGB> @@ -164,42 +211,63 @@ void qt_qimageScaleAARGBA_down_xy_sse4(QImageScaleInfo *isi, unsigned int *dest, int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - const __m128i vCy = _mm_set1_epi32(Cy); - const __m128i vyap = _mm_set1_epi32(yap); - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const int Cx = xapoints[x] >> 16; - const int xap = xapoints[x] & 0xffff; - const __m128i vCx = _mm_set1_epi32(Cx); - const __m128i vxap = _mm_set1_epi32(xap); - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - __m128i vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); - __m128i vr = _mm_mullo_epi32(_mm_srli_epi32(vx, 4), vyap); - - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; + const __m128i vCy = _mm_set1_epi32(Cy); + const __m128i vyap = _mm_set1_epi32(yap); + + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const int Cx = xapoints[x] >> 16; + const int xap = xapoints[x] & 0xffff; + const __m128i vCx = _mm_set1_epi32(Cx); + const __m128i vxap = _mm_set1_epi32(xap); + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + __m128i vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); + __m128i vr = _mm_mullo_epi32(_mm_srli_epi32(vx, 4), vyap); + + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); + vr = _mm_add_epi32(vr, _mm_mullo_epi32(_mm_srli_epi32(vx, 4), vCy)); + } sptr += sow; vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); - vr = _mm_add_epi32(vr, _mm_mullo_epi32(_mm_srli_epi32(vx, 4), vCy)); + vr = _mm_add_epi32(vr, _mm_mullo_epi32(_mm_srli_epi32(vx, 4), _mm_set1_epi32(j))); + + vr = _mm_srli_epi32(vr, 24); + vr = _mm_packus_epi32(vr, _mm_setzero_si128()); + vr = _mm_packus_epi16(vr, _mm_setzero_si128()); + *dptr = _mm_cvtsi128_si32(vr); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - sptr += sow; - vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); - vr = _mm_add_epi32(vr, _mm_mullo_epi32(_mm_srli_epi32(vx, 4), _mm_set1_epi32(j))); - - vr = _mm_srli_epi32(vr, 24); - vr = _mm_packus_epi32(vr, _mm_setzero_si128()); - vr = _mm_packus_epi16(vr, _mm_setzero_si128()); - *dptr = _mm_cvtsi128_si32(vr); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template void qt_qimageScaleAARGBA_up_x_down_y_sse4<false>(QImageScaleInfo *isi, unsigned int *dest, diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 11623c78f0..e3cdc218b2 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -1603,25 +1603,6 @@ QPainterPath QPainterPath::toReversed() const } /*! - \overload - - Converts the path into a list of polygons without any transformation, - and returns the list. - - This function creates one polygon for each subpath regardless of - intersecting subpaths (i.e. overlapping bounding rectangles). To - make sure that such overlapping subpaths are filled correctly, use - the toFillPolygons() function instead. - - \sa toFillPolygons(), toFillPolygon(), {QPainterPath#QPainterPath - Conversion}{QPainterPath Conversion} -*/ -QList<QPolygonF> QPainterPath::toSubpathPolygons() const -{ - return toSubpathPolygons(QTransform()); -} - -/*! Converts the path into a list of polygons using the QTransform \a matrix, and returns the list. @@ -1679,35 +1660,6 @@ QList<QPolygonF> QPainterPath::toSubpathPolygons(const QTransform &matrix) const } /*! - \overload - - Converts the path into a list of polygons without any transformation, - and returns the list. - - The function differs from the toFillPolygon() function in that it - creates several polygons. It is provided because it is usually - faster to draw several small polygons than to draw one large - polygon, even though the total number of points drawn is the same. - - The toFillPolygons() function differs from the toSubpathPolygons() - function in that it create only polygon for subpaths that have - overlapping bounding rectangles. - - Like the toFillPolygon() function, this function uses a rewinding - technique to make sure that overlapping subpaths can be filled - using the correct fill rule. Note that rewinding inserts addition - lines in the polygons so the outline of the fill polygon does not - match the outline of the path. - - \sa toSubpathPolygons(), toFillPolygon(), - {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion} -*/ -QList<QPolygonF> QPainterPath::toFillPolygons() const -{ - return toFillPolygons(QTransform()); -} - -/*! Converts the path into a list of polygons using the QTransform \a matrix, and returns the list. @@ -2903,28 +2855,6 @@ void QPainterPathStroker::setDashOffset(qreal offset) } /*! - \overload - - Converts the path into a polygon without any transformation, - and returns the polygon. - - The polygon is created by first converting all subpaths to - polygons, then using a rewinding technique to make sure that - overlapping subpaths can be filled using the correct fill rule. - - Note that rewinding inserts addition lines in the polygon so - the outline of the fill polygon does not match the outline of - the path. - - \sa toSubpathPolygons(), toFillPolygons(), - {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion} -*/ -QPolygonF QPainterPath::toFillPolygon() const -{ - return toFillPolygon(QTransform()); -} - -/*! Converts the path into a polygon using the QTransform \a matrix, and returns the polygon. diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index 078b665222..aa43c74bee 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -41,6 +41,7 @@ #define QPAINTERPATH_H #include <QtGui/qtguiglobal.h> +#include <QtGui/qtransform.h> #include <QtCore/qglobal.h> #include <QtCore/qrect.h> #include <QtCore/qline.h> @@ -176,12 +177,9 @@ public: Q_REQUIRED_RESULT QPainterPath toReversed() const; - QList<QPolygonF> toSubpathPolygons() const; - QList<QPolygonF> toSubpathPolygons(const QTransform &matrix) const; - QList<QPolygonF> toFillPolygons() const; - QList<QPolygonF> toFillPolygons(const QTransform &matrix) const; - QPolygonF toFillPolygon() const; - QPolygonF toFillPolygon(const QTransform &matrix) const; + QList<QPolygonF> toSubpathPolygons(const QTransform &matrix = QTransform()) const; + QList<QPolygonF> toFillPolygons(const QTransform &matrix = QTransform()) const; + QPolygonF toFillPolygon(const QTransform &matrix = QTransform()) const; int elementCount() const; QPainterPath::Element elementAt(int i) const; @@ -356,6 +354,8 @@ inline void QPainterPath::translate(const QPointF &offset) inline QPainterPath QPainterPath::translated(const QPointF &offset) const { return translated(offset.x(), offset.y()); } +inline QPainterPath operator *(const QPainterPath &p, const QTransform &m) +{ return m.map(p); } #ifndef QT_NO_DEBUG_STREAM Q_GUI_EXPORT QDebug operator<<(QDebug, const QPainterPath &); diff --git a/src/gui/painting/qrasterizer.cpp b/src/gui/painting/qrasterizer.cpp index cd31d75f83..f3c193d799 100644 --- a/src/gui/painting/qrasterizer.cpp +++ b/src/gui/painting/qrasterizer.cpp @@ -46,6 +46,8 @@ #include <private/qdatabuffer_p.h> #include <private/qdrawhelper_p.h> +#include <QtGui/qpainterpath.h> + #include <algorithm> QT_BEGIN_NAMESPACE diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index e906397520..3728060caf 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -43,6 +43,8 @@ #include "private/qfontengine_p.h" #include "private/qnumeric_p.h" +#include <QtGui/qpainterpath.h> + QT_BEGIN_NAMESPACE // #define CACHE_DEBUG diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h index 351f2b2e4e..d235f15029 100644 --- a/src/gui/painting/qtransform.h +++ b/src/gui/painting/qtransform.h @@ -40,7 +40,6 @@ #define QTRANSFORM_H #include <QtGui/qtguiglobal.h> -#include <QtGui/qpainterpath.h> #include <QtGui/qpolygon.h> #include <QtGui/qregion.h> #include <QtGui/qwindowdefs.h> @@ -50,8 +49,8 @@ QT_BEGIN_NAMESPACE - class QVariant; +class QPainterPath; class Q_GUI_EXPORT QTransform { @@ -406,8 +405,6 @@ inline QPolygonF operator *(const QPolygonF &a, const QTransform &m) { return m.map(a); } inline QRegion operator *(const QRegion &r, const QTransform &m) { return m.map(r); } -inline QPainterPath operator *(const QPainterPath &p, const QTransform &m) -{ return m.map(p); } inline QTransform operator *(const QTransform &a, qreal n) { QTransform t(a); t *= n; return t; } diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index c805e23ad0..83c1e8eaa2 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -2885,16 +2885,57 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOff \return a shader resource binding for the given binding number, pipeline stages, texture, and sampler specified by \a binding, \a stage, \a tex, \a sampler. + + \note This function is equivalent to calling sampledTextures() with a + \c count of 1. + + \sa sampledTextures() */ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture( int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler) { + const TextureAndSampler texSampler = { tex, sampler }; + return sampledTextures(binding, stage, 1, &texSampler); +} + +/*! + \return a shader resource binding for the given binding number, pipeline + stages, and the array of texture-sampler pairs specified by \a binding, \a + stage, \a count, and \a texSamplers. + + \note \a count must be at least 1, and not larger than 16. + + \note When \a count is 1, this function is equivalent to sampledTexture(). + + This function is relevant when arrays of combined image samplers are + involved. For example, in GLSL \c{layout(binding = 5) uniform sampler2D + shadowMaps[8];} declares an array of combined image samplers. The + application is then expected provide a QRhiShaderResourceBinding for + binding point 5, set up by calling this function with \a count set to 8 and + a valid texture and sampler for each element of the array. + + \warning All elements of the array must be specified. With the above + example, the only valid, portable approach is calling this function with a + \a count of 8. Additionally, all QRhiTexture and QRhiSampler instances must + be valid, meaning nullptr is not an accepted value. This is due to some of + the underlying APIs, such as, Vulkan, that require a valid image and + sampler object for each element in descriptor arrays. Applications are + advised to provide "dummy" samplers and textures if some array elements are + not relevant (due to not being accessed in the shader). + + \sa sampledTexture() + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTextures( + int binding, StageFlags stage, int count, const TextureAndSampler *texSamplers) +{ + Q_ASSERT(count >= 1 && count <= Data::MAX_TEX_SAMPLER_ARRAY_SIZE); QRhiShaderResourceBinding b; b.d.binding = binding; b.d.stage = stage; b.d.type = SampledTexture; - b.d.u.stex.tex = tex; - b.d.u.stex.sampler = sampler; + b.d.u.stex.count = count; + for (int i = 0; i < count; ++i) + b.d.u.stex.texSamplers[i] = texSamplers[i]; return b; } @@ -3084,10 +3125,14 @@ bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind } break; case QRhiShaderResourceBinding::SampledTexture: - if (da->u.stex.tex != db->u.stex.tex - || da->u.stex.sampler != db->u.stex.sampler) - { + if (da->u.stex.count != db->u.stex.count) return false; + for (int i = 0; i < da->u.stex.count; ++i) { + if (da->u.stex.texSamplers[i].tex != db->u.stex.texSamplers[i].tex + || da->u.stex.texSamplers[i].sampler != db->u.stex.texSamplers[i].sampler) + { + return false; + } } break; case QRhiShaderResourceBinding::ImageLoad: @@ -3162,10 +3207,13 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b) << ')'; break; case QRhiShaderResourceBinding::SampledTexture: - dbg.nospace() << " SampledTexture(" - << "texture=" << d->u.stex.tex - << " sampler=" << d->u.stex.sampler - << ')'; + dbg.nospace() << " SampledTextures(" + << "count=" << d->u.stex.count; + for (int i = 0; i < d->u.stex.count; ++i) { + dbg.nospace() << " texture=" << d->u.stex.texSamplers[i].tex + << " sampler=" << d->u.stex.texSamplers[i].sampler; + } + dbg.nospace() << ')'; break; case QRhiShaderResourceBinding::ImageLoad: dbg.nospace() << " ImageLoad(" diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 17c911a5ff..9d906d7bbd 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -348,6 +348,12 @@ public: static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler); + struct TextureAndSampler { + QRhiTexture *tex; + QRhiSampler *sampler; + }; + static QRhiShaderResourceBinding sampledTextures(int binding, StageFlags stage, int count, const TextureAndSampler *texSamplers); + static QRhiShaderResourceBinding imageLoad(int binding, StageFlags stage, QRhiTexture *tex, int level); static QRhiShaderResourceBinding imageStore(int binding, StageFlags stage, QRhiTexture *tex, int level); static QRhiShaderResourceBinding imageLoadStore(int binding, StageFlags stage, QRhiTexture *tex, int level); @@ -370,9 +376,10 @@ public: int maybeSize; bool hasDynamicOffset; }; + static const int MAX_TEX_SAMPLER_ARRAY_SIZE = 16; struct SampledTextureData { - QRhiTexture *tex; - QRhiSampler *sampler; + int count; + TextureAndSampler texSamplers[MAX_TEX_SAMPLER_ARRAY_SIZE]; }; struct StorageImageData { QRhiTexture *tex; diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 7b583e6fd2..ed202958f3 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -113,6 +113,10 @@ QT_BEGIN_NAMESPACE #define DXGI_ADAPTER_FLAG_SOFTWARE 2 #endif +#ifndef D3D11_1_UAV_SLOT_COUNT +#define D3D11_1_UAV_SLOT_COUNT 64 +#endif + QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice) : ofr(this), deviceCurse(this) @@ -627,18 +631,25 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind break; case QRhiShaderResourceBinding::SampledTexture: { - QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex); - QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler); - if (texD->generation != bd.stex.texGeneration - || texD->m_id != bd.stex.texId - || samplerD->generation != bd.stex.samplerGeneration - || samplerD->m_id != bd.stex.samplerId) - { + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + if (bd.stex.count != data->count) { + bd.stex.count = data->count; srbUpdate = true; - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; + } + for (int elem = 0; elem < data->count; ++elem) { + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex); + QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler); + if (texD->generation != bd.stex.d[elem].texGeneration + || texD->m_id != bd.stex.d[elem].texId + || samplerD->generation != bd.stex.d[elem].samplerGeneration + || samplerD->m_id != bd.stex.d[elem].samplerId) + { + srbUpdate = true; + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + } } } break; @@ -1894,31 +1905,38 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD, break; case QRhiShaderResourceBinding::SampledTexture: { - QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex); - QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler); - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; - if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { - QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps); - if (nativeBinding.first >= 0 && nativeBinding.second >= 0) { - res[RBM_VERTEX].textures.append({ nativeBinding.first, texD->srv }); - res[RBM_VERTEX].samplers.append({ nativeBinding.second, samplerD->samplerState }); + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + bd.stex.count = data->count; + const QPair<int, int> nativeBindingVert = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps); + const QPair<int, int> nativeBindingFrag = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps); + const QPair<int, int> nativeBindingComp = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps); + // if SPIR-V binding b is mapped to tN and sN in HLSL, and it + // is an array, then it will use tN, tN+1, tN+2, ..., and sN, + // sN+1, sN+2, ... + for (int elem = 0; elem < data->count; ++elem) { + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex); + QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler); + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { + if (nativeBindingVert.first >= 0 && nativeBindingVert.second >= 0) { + res[RBM_VERTEX].textures.append({ nativeBindingVert.first + elem, texD->srv }); + res[RBM_VERTEX].samplers.append({ nativeBindingVert.second + elem, samplerD->samplerState }); + } } - } - if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { - QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps); - if (nativeBinding.first >= 0 && nativeBinding.second >= 0) { - res[RBM_FRAGMENT].textures.append({ nativeBinding.first, texD->srv }); - res[RBM_FRAGMENT].samplers.append({ nativeBinding.second, samplerD->samplerState }); + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { + if (nativeBindingFrag.first >= 0 && nativeBindingFrag.second >= 0) { + res[RBM_FRAGMENT].textures.append({ nativeBindingFrag.first + elem, texD->srv }); + res[RBM_FRAGMENT].samplers.append({ nativeBindingFrag.second + elem, samplerD->samplerState }); + } } - } - if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { - QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps); - if (nativeBinding.first >= 0 && nativeBinding.second >= 0) { - res[RBM_COMPUTE].textures.append({ nativeBinding.first, texD->srv }); - res[RBM_COMPUTE].samplers.append({ nativeBinding.second, samplerD->samplerState }); + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + if (nativeBindingComp.first >= 0 && nativeBindingComp.second >= 0) { + res[RBM_COMPUTE].textures.append({ nativeBindingComp.first + elem, texD->srv }); + res[RBM_COMPUTE].samplers.append({ nativeBindingComp.second + elem, samplerD->samplerState }); + } } } } @@ -2077,102 +2095,156 @@ static void applyDynamicOffsets(QVarLengthArray<UINT, 4> *offsets, } } +static inline uint clampedResourceCount(uint startSlot, int countSlots, uint maxSlots, const char *resType) +{ + if (startSlot + countSlots > maxSlots) { + qWarning("Not enough D3D11 %s slots to bind %d resources starting at slot %d, max slots is %d", + resType, countSlots, startSlot, maxSlots); + countSlots = maxSlots > startSlot ? maxSlots - startSlot : 0; + } + return countSlots; +} + void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD, const uint *dynOfsPairs, int dynOfsPairCount, bool offsetOnlyChange) { if (!offsetOnlyChange) { - for (const auto &batch : srbD->vssamplers.batches) - context->VSSetSamplers(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); + for (const auto &batch : srbD->vssamplers.batches) { + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "VS sampler"); + if (count) + context->VSSetSamplers(batch.startBinding, count, batch.resources.constData()); + } for (const auto &batch : srbD->vsshaderresources.batches) { - context->VSSetShaderResources(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); - contextState.vsHighestActiveSrvBinding = qMax<int>(contextState.vsHighestActiveSrvBinding, - int(batch.startBinding) + batch.resources.count() - 1); + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "VS SRV"); + if (count) { + context->VSSetShaderResources(batch.startBinding, count, batch.resources.constData()); + contextState.vsHighestActiveSrvBinding = qMax(contextState.vsHighestActiveSrvBinding, + int(batch.startBinding + count) - 1); + } } - for (const auto &batch : srbD->fssamplers.batches) - context->PSSetSamplers(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); + for (const auto &batch : srbD->fssamplers.batches) { + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "PS sampler"); + if (count) + context->PSSetSamplers(batch.startBinding, count, batch.resources.constData()); + } for (const auto &batch : srbD->fsshaderresources.batches) { - context->PSSetShaderResources(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); - contextState.fsHighestActiveSrvBinding = qMax<int>(contextState.fsHighestActiveSrvBinding, - int(batch.startBinding) + batch.resources.count() - 1); + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "PS SRV"); + if (count) { + context->PSSetShaderResources(batch.startBinding, count, batch.resources.constData()); + contextState.fsHighestActiveSrvBinding = qMax(contextState.fsHighestActiveSrvBinding, + int(batch.startBinding + count) - 1); + } } - for (const auto &batch : srbD->cssamplers.batches) - context->CSSetSamplers(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); + for (const auto &batch : srbD->cssamplers.batches) { + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "CS sampler"); + if (count) + context->CSSetSamplers(batch.startBinding, count, batch.resources.constData()); + } for (const auto &batch : srbD->csshaderresources.batches) { - context->CSSetShaderResources(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); - contextState.csHighestActiveSrvBinding = qMax<int>(contextState.csHighestActiveSrvBinding, - int(batch.startBinding) + batch.resources.count() - 1); + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "CS SRV"); + if (count) { + context->CSSetShaderResources(batch.startBinding, count, batch.resources.constData()); + contextState.csHighestActiveSrvBinding = qMax(contextState.csHighestActiveSrvBinding, + int(batch.startBinding + count) - 1); + } } } for (int i = 0, ie = srbD->vsubufs.batches.count(); i != ie; ++i) { - if (!dynOfsPairCount) { - context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, - UINT(srbD->vsubufs.batches[i].resources.count()), - srbD->vsubufs.batches[i].resources.constData(), - srbD->vsubufoffsets.batches[i].resources.constData(), - srbD->vsubufsizes.batches[i].resources.constData()); - } else { - QVarLengthArray<UINT, 4> offsets; - applyDynamicOffsets(&offsets, i, &srbD->vsubufs, &srbD->vsubufoffsets, dynOfsPairs, dynOfsPairCount); - context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, - UINT(srbD->vsubufs.batches[i].resources.count()), - srbD->vsubufs.batches[i].resources.constData(), - offsets.constData(), - srbD->vsubufsizes.batches[i].resources.constData()); + const uint count = clampedResourceCount(srbD->vsubufs.batches[i].startBinding, + srbD->vsubufs.batches[i].resources.count(), + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, + "VS cbuf"); + if (count) { + if (!dynOfsPairCount) { + context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, + count, + srbD->vsubufs.batches[i].resources.constData(), + srbD->vsubufoffsets.batches[i].resources.constData(), + srbD->vsubufsizes.batches[i].resources.constData()); + } else { + QVarLengthArray<UINT, 4> offsets; + applyDynamicOffsets(&offsets, i, &srbD->vsubufs, &srbD->vsubufoffsets, dynOfsPairs, dynOfsPairCount); + context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, + count, + srbD->vsubufs.batches[i].resources.constData(), + offsets.constData(), + srbD->vsubufsizes.batches[i].resources.constData()); + } } } for (int i = 0, ie = srbD->fsubufs.batches.count(); i != ie; ++i) { - if (!dynOfsPairCount) { - context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, - UINT(srbD->fsubufs.batches[i].resources.count()), - srbD->fsubufs.batches[i].resources.constData(), - srbD->fsubufoffsets.batches[i].resources.constData(), - srbD->fsubufsizes.batches[i].resources.constData()); - } else { - QVarLengthArray<UINT, 4> offsets; - applyDynamicOffsets(&offsets, i, &srbD->fsubufs, &srbD->fsubufoffsets, dynOfsPairs, dynOfsPairCount); - context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, - UINT(srbD->fsubufs.batches[i].resources.count()), - srbD->fsubufs.batches[i].resources.constData(), - offsets.constData(), - srbD->fsubufsizes.batches[i].resources.constData()); + const uint count = clampedResourceCount(srbD->fsubufs.batches[i].startBinding, + srbD->fsubufs.batches[i].resources.count(), + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, + "PS cbuf"); + if (count) { + if (!dynOfsPairCount) { + context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, + count, + srbD->fsubufs.batches[i].resources.constData(), + srbD->fsubufoffsets.batches[i].resources.constData(), + srbD->fsubufsizes.batches[i].resources.constData()); + } else { + QVarLengthArray<UINT, 4> offsets; + applyDynamicOffsets(&offsets, i, &srbD->fsubufs, &srbD->fsubufoffsets, dynOfsPairs, dynOfsPairCount); + context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, + count, + srbD->fsubufs.batches[i].resources.constData(), + offsets.constData(), + srbD->fsubufsizes.batches[i].resources.constData()); + } } } for (int i = 0, ie = srbD->csubufs.batches.count(); i != ie; ++i) { - if (!dynOfsPairCount) { - context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, - UINT(srbD->csubufs.batches[i].resources.count()), - srbD->csubufs.batches[i].resources.constData(), - srbD->csubufoffsets.batches[i].resources.constData(), - srbD->csubufsizes.batches[i].resources.constData()); - } else { - QVarLengthArray<UINT, 4> offsets; - applyDynamicOffsets(&offsets, i, &srbD->csubufs, &srbD->csubufoffsets, dynOfsPairs, dynOfsPairCount); - context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, - UINT(srbD->csubufs.batches[i].resources.count()), - srbD->csubufs.batches[i].resources.constData(), - offsets.constData(), - srbD->csubufsizes.batches[i].resources.constData()); + const uint count = clampedResourceCount(srbD->csubufs.batches[i].startBinding, + srbD->csubufs.batches[i].resources.count(), + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, + "CS cbuf"); + if (count) { + if (!dynOfsPairCount) { + context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, + count, + srbD->csubufs.batches[i].resources.constData(), + srbD->csubufoffsets.batches[i].resources.constData(), + srbD->csubufsizes.batches[i].resources.constData()); + } else { + QVarLengthArray<UINT, 4> offsets; + applyDynamicOffsets(&offsets, i, &srbD->csubufs, &srbD->csubufoffsets, dynOfsPairs, dynOfsPairCount); + context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, + count, + srbD->csubufs.batches[i].resources.constData(), + offsets.constData(), + srbD->csubufsizes.batches[i].resources.constData()); + } } } - for (int i = 0, ie = srbD->csUAVs.batches.count(); i != ie; ++i) { - const uint startBinding = srbD->csUAVs.batches[i].startBinding; - const uint count = uint(srbD->csUAVs.batches[i].resources.count()); - context->CSSetUnorderedAccessViews(startBinding, - count, - srbD->csUAVs.batches[i].resources.constData(), - nullptr); - contextState.csHighestActiveUavBinding = qMax<int>(contextState.csHighestActiveUavBinding, - int(startBinding + count - 1)); + for (const auto &batch : srbD->csUAVs.batches) { + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_1_UAV_SLOT_COUNT, "CS UAV"); + if (count) { + context->CSSetUnorderedAccessViews(batch.startBinding, + count, + batch.resources.constData(), + nullptr); + contextState.csHighestActiveUavBinding = qMax(contextState.csHighestActiveUavBinding, + int(batch.startBinding + count) - 1); + } } } @@ -3529,11 +3601,15 @@ static pD3DCompile resolveD3DCompile() static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error, QShaderKey *usedShaderKey) { - QShaderCode dxbc = shader.shader({ QShader::DxbcShader, 50, shaderVariant }); - if (!dxbc.shader().isEmpty()) + QShaderKey key = { QShader::DxbcShader, 50, shaderVariant }; + QShaderCode dxbc = shader.shader(key); + if (!dxbc.shader().isEmpty()) { + if (usedShaderKey) + *usedShaderKey = key; return dxbc.shader(); + } - const QShaderKey key = { QShader::HlslShader, 50, shaderVariant }; + key = { QShader::HlslShader, 50, shaderVariant }; QShaderCode hlslSource = shader.shader(key); if (hlslSource.shader().isEmpty()) { qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader; diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index f749b612b5..33412b8011 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -210,10 +210,13 @@ struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings uint generation; }; struct BoundSampledTextureData { - quint64 texId; - uint texGeneration; - quint64 samplerId; - uint samplerGeneration; + int count; + struct { + quint64 texId; + uint texGeneration; + quint64 samplerId; + uint samplerGeneration; + } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE]; }; struct BoundStorageImageData { quint64 id; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 4a442bc582..ea9bce08e4 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -917,10 +917,12 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind hasDynamicOffsetInSrb = true; break; case QRhiShaderResourceBinding::SampledTexture: - trackedRegisterTexture(&passResTracker, - QRHI_RES(QGles2Texture, b->u.stex.tex), - QRhiPassResourceTracker::TexSample, - QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage)); + for (int elem = 0; elem < b->u.stex.count; ++elem) { + trackedRegisterTexture(&passResTracker, + QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex), + QRhiPassResourceTracker::TexSample, + QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage)); + } break; case QRhiShaderResourceBinding::ImageLoad: case QRhiShaderResourceBinding::ImageStore: @@ -2572,36 +2574,37 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC break; case QRhiShaderResourceBinding::SampledTexture: { - QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.tex); - QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.sampler); QVector<QGles2SamplerDescription> &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers); - - for (QGles2SamplerDescription &sampler : samplers) { - if (sampler.binding == b->binding) { - f->glActiveTexture(GL_TEXTURE0 + uint(texUnit)); - f->glBindTexture(texD->target, texD->texture); - - if (texD->samplerState != samplerD->d) { - f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter)); - f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter)); - f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps)); - f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt)); - // 3D textures not supported by GLES 2.0 or by us atm... - //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr); - if (caps.textureCompareMode) { - if (samplerD->d.gltexcomparefunc != GL_NEVER) { - f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc)); - } else { - f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE); + for (int elem = 0; elem < b->u.stex.count; ++elem) { + QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex); + QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.texSamplers[elem].sampler); + for (QGles2SamplerDescription &sampler : samplers) { + if (sampler.binding == b->binding) { + f->glActiveTexture(GL_TEXTURE0 + uint(texUnit)); + f->glBindTexture(texD->target, texD->texture); + + if (texD->samplerState != samplerD->d) { + f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter)); + f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter)); + f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps)); + f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt)); + // 3D textures not supported by GLES 2.0 or by us atm... + //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr); + if (caps.textureCompareMode) { + if (samplerD->d.gltexcomparefunc != GL_NEVER) { + f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc)); + } else { + f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } } + texD->samplerState = samplerD->d; } - texD->samplerState = samplerD->d; - } - f->glUniform1i(sampler.glslLocation, texUnit); - ++texUnit; + f->glUniform1i(sampler.glslLocation + elem, texUnit); + ++texUnit; + } } } } diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 314c58b0b7..0806c8a052 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -748,30 +748,33 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD break; case QRhiShaderResourceBinding::SampledTexture: { - QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); - QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); - if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { - const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture); - const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler); - if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { - res[VERTEX].textures.append({ nativeBindingTexture, texD->d->tex }); - res[VERTEX].samplers.append({ nativeBindingSampler, samplerD->d->samplerState }); + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + for (int elem = 0; elem < data->count; ++elem) { + QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex); + QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler); + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { + const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture); + const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler); + if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { + res[VERTEX].textures.append({ nativeBindingTexture + elem, texD->d->tex }); + res[VERTEX].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState }); + } } - } - if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { - const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture); - const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler); - if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { - res[FRAGMENT].textures.append({ nativeBindingTexture, texD->d->tex }); - res[FRAGMENT].samplers.append({ nativeBindingSampler, samplerD->d->samplerState }); + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { + const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture); + const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler); + if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { + res[FRAGMENT].textures.append({ nativeBindingTexture + elem, texD->d->tex }); + res[FRAGMENT].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState }); + } } - } - if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { - const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture); - const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler); - if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { - res[COMPUTE].textures.append({ nativeBindingTexture, texD->d->tex }); - res[COMPUTE].samplers.append({ nativeBindingSampler, samplerD->d->samplerState }); + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture); + const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler); + if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { + res[COMPUTE].textures.append({ nativeBindingTexture + elem, texD->d->tex }); + res[COMPUTE].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState }); + } } } } @@ -1020,21 +1023,28 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind break; case QRhiShaderResourceBinding::SampledTexture: { - QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); - QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); - if (texD->generation != bd.stex.texGeneration - || texD->m_id != bd.stex.texId - || samplerD->generation != bd.stex.samplerGeneration - || samplerD->m_id != bd.stex.samplerId) - { + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + if (bd.stex.count != data->count) { + bd.stex.count = data->count; resNeedsRebind = true; - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; } - texD->lastActiveFrameSlot = currentFrameSlot; - samplerD->lastActiveFrameSlot = currentFrameSlot; + for (int elem = 0; elem < data->count; ++elem) { + QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex); + QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler); + if (texD->generation != bd.stex.d[elem].texGeneration + || texD->m_id != bd.stex.d[elem].texId + || samplerD->generation != bd.stex.d[elem].samplerGeneration + || samplerD->m_id != bd.stex.d[elem].samplerId) + { + resNeedsRebind = true; + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + } + texD->lastActiveFrameSlot = currentFrameSlot; + samplerD->lastActiveFrameSlot = currentFrameSlot; + } } break; case QRhiShaderResourceBinding::ImageLoad: @@ -2981,12 +2991,16 @@ bool QMetalShaderResourceBindings::build() break; case QRhiShaderResourceBinding::SampledTexture: { - QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); - QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + bd.stex.count = data->count; + for (int elem = 0; elem < data->count; ++elem) { + QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex); + QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler); + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + } } break; case QRhiShaderResourceBinding::ImageLoad: @@ -3241,8 +3255,12 @@ static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c) id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant, QString *error, QByteArray *entryPoint, QShaderKey *activeKey) { - QShaderKey key = { QShader::MetalLibShader, 12, shaderVariant }; + QShaderKey key = { QShader::MetalLibShader, 20, shaderVariant }; QShaderCode mtllib = shader.shader(key); + if (mtllib.shader().isEmpty()) { + key.setSourceVersion(12); + mtllib = shader.shader(key); + } if (!mtllib.shader().isEmpty()) { dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(), size_t(mtllib.shader().size()), @@ -3261,16 +3279,20 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var } } - key = { QShader::MslShader, 12, shaderVariant }; + key = { QShader::MslShader, 20, shaderVariant }; QShaderCode mslSource = shader.shader(key); if (mslSource.shader().isEmpty()) { - qWarning() << "No MSL 1.2 code found in baked shader" << shader; + key.setSourceVersion(12); + mslSource = shader.shader(key); + } + if (mslSource.shader().isEmpty()) { + qWarning() << "No MSL 2.0 or 1.2 code found in baked shader" << shader; return nil; } NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()]; MTLCompileOptions *opts = [[MTLCompileOptions alloc] init]; - opts.languageVersion = MTLLanguageVersion1_2; + opts.languageVersion = key.sourceVersion() == 20 ? MTLLanguageVersion2_0 : MTLLanguageVersion1_2; NSError *err = nil; id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err]; [opts release]; diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index a5af5611a6..cb4b777d88 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -197,10 +197,13 @@ struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings uint generation; }; struct BoundSampledTextureData { - quint64 texId; - uint texGeneration; - quint64 samplerId; - uint samplerGeneration; + int count; + struct { + quint64 texId; + uint texGeneration; + quint64 samplerId; + uint samplerGeneration; + } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE]; }; struct BoundStorageImageData { quint64 id; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index a92c3e14e9..26c153afff 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -345,6 +345,15 @@ static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTyp return true; } + // In certain cases allocateDescriptorSet() will attempt to allocate from a + // pool that does not have enough descriptors of a certain type. This makes + // the validation layer shout. However, this is not an error since we will + // then move on to another pool. If there is a real error, a qWarning + // message is shown by allocateDescriptorSet(), so the validation warning + // does not have any value and is just noise. + if (strstr(pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307")) + return true; + return false; } @@ -2487,7 +2496,8 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb); QVarLengthArray<VkDescriptorBufferInfo, 8> bufferInfos; - QVarLengthArray<VkDescriptorImageInfo, 8> imageInfos; + using ArrayOfImageDesc = QVarLengthArray<VkDescriptorImageInfo, 8>; + QVarLengthArray<ArrayOfImageDesc, 8> imageInfos; QVarLengthArray<VkWriteDescriptorSet, 12> writeInfos; QVarLengthArray<QPair<int, int>, 12> infoIndices; @@ -2530,17 +2540,22 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i break; case QRhiShaderResourceBinding::SampledTexture: { - QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex); - QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler); + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + writeInfo.descriptorCount = data->count; // arrays of combined image samplers are supported writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; - VkDescriptorImageInfo imageInfo; - imageInfo.sampler = samplerD->sampler; - imageInfo.imageView = texD->imageView; - imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + ArrayOfImageDesc imageInfo(data->count); + for (int elem = 0; elem < data->count; ++elem) { + QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex); + QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler); + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + imageInfo[elem].sampler = samplerD->sampler; + imageInfo[elem].imageView = texD->imageView; + imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + bd.stex.count = data->count; imageInfoIndex = imageInfos.count(); imageInfos.append(imageInfo); } @@ -2555,10 +2570,10 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bd.simage.id = texD->m_id; bd.simage.generation = texD->generation; - VkDescriptorImageInfo imageInfo; - imageInfo.sampler = VK_NULL_HANDLE; - imageInfo.imageView = view; - imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + ArrayOfImageDesc imageInfo(1); + imageInfo[0].sampler = VK_NULL_HANDLE; + imageInfo[0].imageView = view; + imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL; imageInfoIndex = imageInfos.count(); imageInfos.append(imageInfo); } @@ -2596,7 +2611,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i if (bufferInfoIndex >= 0) writeInfos[i].pBufferInfo = &bufferInfos[bufferInfoIndex]; else if (imageInfoIndex >= 0) - writeInfos[i].pImageInfo = &imageInfos[imageInfoIndex]; + writeInfos[i].pImageInfo = imageInfos[imageInfoIndex].constData(); } df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.count()), writeInfos.constData(), 0, nullptr); @@ -4210,24 +4225,30 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin break; case QRhiShaderResourceBinding::SampledTexture: { - QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex); - QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler); - texD->lastActiveFrameSlot = currentFrameSlot; - samplerD->lastActiveFrameSlot = currentFrameSlot; - trackedRegisterTexture(&passResTracker, texD, - QRhiPassResourceTracker::TexSample, - QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage)); - - if (texD->generation != bd.stex.texGeneration - || texD->m_id != bd.stex.texId - || samplerD->generation != bd.stex.samplerGeneration - || samplerD->m_id != bd.stex.samplerId) - { + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + if (bd.stex.count != data->count) { + bd.stex.count = data->count; rewriteDescSet = true; - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; + } + for (int elem = 0; elem < data->count; ++elem) { + QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex); + QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler); + texD->lastActiveFrameSlot = currentFrameSlot; + samplerD->lastActiveFrameSlot = currentFrameSlot; + trackedRegisterTexture(&passResTracker, texD, + QRhiPassResourceTracker::TexSample, + QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage)); + if (texD->generation != bd.stex.d[elem].texGeneration + || texD->m_id != bd.stex.d[elem].texId + || samplerD->generation != bd.stex.d[elem].samplerGeneration + || samplerD->m_id != bd.stex.d[elem].samplerId) + { + rewriteDescSet = true; + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + } } } break; @@ -6065,7 +6086,10 @@ bool QVkShaderResourceBindings::build() memset(&vkbinding, 0, sizeof(vkbinding)); vkbinding.binding = uint32_t(b->binding); vkbinding.descriptorType = toVkDescriptorType(b); - vkbinding.descriptorCount = 1; // no array support yet + if (b->type == QRhiShaderResourceBinding::SampledTexture) + vkbinding.descriptorCount = b->u.stex.count; + else + vkbinding.descriptorCount = 1; vkbinding.stageFlags = toVkShaderStageFlags(b->stage); vkbindings.append(vkbinding); } diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index fd65417e75..62516e268d 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -254,10 +254,13 @@ struct QVkShaderResourceBindings : public QRhiShaderResourceBindings uint generation; }; struct BoundSampledTextureData { - quint64 texId; - uint texGeneration; - quint64 samplerId; - uint samplerGeneration; + int count; + struct { + quint64 texId; + uint texGeneration; + quint64 samplerId; + uint samplerGeneration; + } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE]; }; struct BoundStorageImageData { quint64 id; diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp index 884525bd76..b4b60cbaaf 100644 --- a/src/gui/text/qrawfont.cpp +++ b/src/gui/text/qrawfont.cpp @@ -51,6 +51,7 @@ #include <QtCore/qendian.h> #include <QtCore/qfile.h> +#include <QtGui/qpainterpath.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/vulkan/qvulkanwindow.cpp b/src/gui/vulkan/qvulkanwindow.cpp index ee49cf0999..cb89b0b1e6 100644 --- a/src/gui/vulkan/qvulkanwindow.cpp +++ b/src/gui/vulkan/qvulkanwindow.cpp @@ -1602,7 +1602,7 @@ bool QVulkanWindow::event(QEvent *e) \since 5.15 */ -void QVulkanWindow::setQueueCreateInfoModifier(QueueCreateInfoModifier modifier) +void QVulkanWindow::setQueueCreateInfoModifier(const QueueCreateInfoModifier &modifier) { Q_D(QVulkanWindow); d->queueCreateInfoModifier = modifier; diff --git a/src/gui/vulkan/qvulkanwindow.h b/src/gui/vulkan/qvulkanwindow.h index 511b9501bd..a2d1c9995c 100644 --- a/src/gui/vulkan/qvulkanwindow.h +++ b/src/gui/vulkan/qvulkanwindow.h @@ -106,7 +106,7 @@ public: typedef std::function<void(const VkQueueFamilyProperties *, uint32_t, QVector<VkDeviceQueueCreateInfo> &)> QueueCreateInfoModifier; - void setQueueCreateInfoModifier(QueueCreateInfoModifier modifier); + void setQueueCreateInfoModifier(const QueueCreateInfoModifier &modifier); bool isValid() const; diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 21d46011d7..d4df58e885 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -253,6 +253,7 @@ static void ensureInitialized() /*! \enum QNetworkAccessManager::NetworkAccessibility + \obsolete Indicates whether the network is accessible via this network access manager. @@ -268,6 +269,7 @@ static void ensureInitialized() /*! \property QNetworkAccessManager::networkAccessible \brief whether the network is currently accessible via this network access manager. + \obsolete \since 4.7 diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index 5194afb1be..f50dfbcca4 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -91,12 +91,15 @@ public: }; #ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section - enum NetworkAccessibility { + enum QT_DEPRECATED_VERSION_5_15 NetworkAccessibility { UnknownAccessibility = -1, NotAccessible = 0, Accessible = 1 }; +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED Q_ENUM(NetworkAccessibility) +QT_WARNING_POP #endif explicit QNetworkAccessManager(QObject *parent = nullptr); @@ -150,8 +153,11 @@ public: QT_DEPRECATED_VERSION_5_15 QNetworkConfiguration configuration() const; QT_DEPRECATED_VERSION_5_15 QNetworkConfiguration activeConfiguration() const; +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED QT_DEPRECATED_VERSION_5_15 void setNetworkAccessible(NetworkAccessibility accessible); QT_DEPRECATED_VERSION_5_15 NetworkAccessibility networkAccessible() const; +QT_WARNING_POP #endif #ifndef QT_NO_SSL @@ -187,7 +193,14 @@ Q_SIGNALS: #ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section QT_DEPRECATED_VERSION_5_15 void networkSessionConnected(); +#ifndef Q_MOC_RUN // moc has trouble with the expansion of these macros +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED +#endif QT_DEPRECATED_VERSION_5_15 void networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible); +#ifndef Q_MOC_RUN // moc has trouble with the expansion of these macros +QT_WARNING_POP +#endif #endif protected: diff --git a/src/network/doc/qtnetwork.qdocconf b/src/network/doc/qtnetwork.qdocconf index 5465b1c0af..8c7f3fc912 100644 --- a/src/network/doc/qtnetwork.qdocconf +++ b/src/network/doc/qtnetwork.qdocconf @@ -27,7 +27,7 @@ qhp.QtNetwork.subprojects.classes.sortPages = true tagfile = ../../../doc/qtnetwork/qtnetwork.tags -depends += qtcore qtgui qtdoc qmake +depends += qtcore qtgui qtdoc qmake qtcmake headerdirs += .. diff --git a/src/network/doc/snippets/CMakeLists.txt b/src/network/doc/snippets/CMakeLists.txt new file mode 100644 index 0000000000..a8030dd260 --- /dev/null +++ b/src/network/doc/snippets/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.1.0) + +project(network_cppsnippets) + +add_executable(mytarget + network/tcpwait.cpp +) + +# ![0] +find_package(Qt5 COMPONENTS Network REQUIRED) +target_link_libraries(mytarget Qt5::Network) +# ![0] diff --git a/src/network/doc/snippets/code/doc_src_qtnetwork.pro b/src/network/doc/snippets/code/doc_src_qtnetwork.pro deleted file mode 100644 index a100943e58..0000000000 --- a/src/network/doc/snippets/code/doc_src_qtnetwork.pro +++ /dev/null @@ -1,3 +0,0 @@ -#! [0] -QT += network -#! [0] diff --git a/src/network/doc/snippets/network/tcpwait.cpp b/src/network/doc/snippets/network/tcpwait.cpp index d57305930c..b81c5c1d53 100644 --- a/src/network/doc/snippets/network/tcpwait.cpp +++ b/src/network/doc/snippets/network/tcpwait.cpp @@ -48,7 +48,7 @@ ** ****************************************************************************/ -#include <QtGui> +#include <QCoreApplication> #include <QTcpSocket> int main(int argv, char **args) diff --git a/src/network/doc/snippets/snippets.pro b/src/network/doc/snippets/snippets.pro index 39153e0c8e..bdf854e454 100644 --- a/src/network/doc/snippets/snippets.pro +++ b/src/network/doc/snippets/snippets.pro @@ -2,6 +2,8 @@ TEMPLATE = app TARGET = network_cppsnippets +# ![0] QT += network +# ![0] SOURCES += network/tcpwait.cpp diff --git a/src/network/doc/src/bearermanagement.qdoc b/src/network/doc/src/bearermanagement.qdoc index f8d6a807d2..8aec894269 100644 --- a/src/network/doc/src/bearermanagement.qdoc +++ b/src/network/doc/src/bearermanagement.qdoc @@ -31,6 +31,8 @@ \title Bearer Management \brief An API to control the system's connectivity state. +\warning Bearer management is deprecated and will be removed in Qt 6.0. + Bearer Management controls the connectivity state of the system so that the user can start or stop interfaces or roam transparently between access points. diff --git a/src/network/doc/src/qtnetwork.qdoc b/src/network/doc/src/qtnetwork.qdoc index 85a3c198a0..57b0210b7f 100644 --- a/src/network/doc/src/qtnetwork.qdoc +++ b/src/network/doc/src/qtnetwork.qdoc @@ -34,19 +34,16 @@ TCP/IP. Operations such as requests, cookies, and sending data over HTTP are handled by various C++ classes. - \section1 Getting Started + \include module-use.qdocinc using qt module + \snippet CMakeLists.txt 0 - To use Qt Network classes,add this directive into the C++ files: - \code - #include <QtNetwork> - \endcode + See also the \l{Build with CMake} overview. - \if !defined(qtforpython) - To link against the Qt Network module, add this line to the project file: - \code - QT += network - \endcode - \endif + \section2 Building with qmake + + Add \c network to the \c QT variable: + + \snippet snippets.pro 0 \section1 Articles and Guides @@ -100,13 +97,6 @@ \qtvariable network \brief Provides classes to make network programming easier and portable. - To include the definitions of the module's classes, use the - following directive: - - \snippet code/doc_src_qtnetwork.cpp 1 - - To link against the module, add this line to your \l qmake \c - .pro file: - - \snippet code/doc_src_qtnetwork.pro 0 + Qt Network provides a set of APIs for programming applications that use + TCP/IP. See the \l{Qt Network} overview for more information. */ diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index da952f6e6b..aa2f8fed02 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -564,12 +564,12 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, int pos = -1; #if QT_CONFIG(regularexpression) - if (syntax == Wildcard) + if (syntax == PatternSyntax::Wildcard) pos = pathPrefix.indexOf(QRegularExpression(QLatin1String("[*?[]"))); - else if (syntax == RegExp) + else if (syntax == PatternSyntax::RegularExpression) pos = sourcePath.indexOf(QRegularExpression(QLatin1String("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]"))); #else - if (syntax == Wildcard || syntax == RegExp) + if (syntax == PatternSyntax::Wildcard || syntax == PatternSyntax::RegExp) qWarning("Regular expression support is disabled in this build. Only fixed string can be searched"); return QList<QSslCertificate>(); #endif @@ -606,7 +606,7 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, QList<QSslCertificate> certs; #if QT_CONFIG(regularexpression) - if (syntax == Wildcard) + if (syntax == PatternSyntax::Wildcard) sourcePath = QRegularExpression::wildcardToRegularExpression(sourcePath); QRegularExpression pattern(QRegularExpression::anchoredPattern(sourcePath)); diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h index c46065160a..525642a809 100644 --- a/src/network/ssl/qsslcertificate.h +++ b/src/network/ssl/qsslcertificate.h @@ -84,8 +84,8 @@ public: EmailAddress }; - enum PatternSyntax { - RegExp, + enum class PatternSyntax { + RegularExpression, Wildcard, FixedString }; diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 5732deaaef..1ce01dbee9 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -130,8 +130,9 @@ public: // Certificate Authority (CA) settings QList<QSslCertificate> caCertificates() const; void setCaCertificates(const QList<QSslCertificate> &certificates); - bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem, - QSslCertificate::PatternSyntax syntax = QSslCertificate::FixedString); + bool addCaCertificates( + const QString &path, QSsl::EncodingFormat format = QSsl::Pem, + QSslCertificate::PatternSyntax syntax = QSslCertificate::PatternSyntax::FixedString); void addCaCertificate(const QSslCertificate &certificate); void addCaCertificates(const QList<QSslCertificate> &certificates); diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp index 2db5c48ff2..c355abad73 100644 --- a/src/network/ssl/qsslsocket_schannel.cpp +++ b/src/network/ssl/qsslsocket_schannel.cpp @@ -1306,7 +1306,8 @@ void QSslSocketBackendPrivate::transmit() int totalRead = 0; bool hadIncompleteData = false; while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) { - if (missingData > plainSocket->bytesAvailable()) { + if (missingData > plainSocket->bytesAvailable() + && (!readBufferMaxSize || readBufferMaxSize >= missingData)) { #ifdef QSSLSOCKET_DEBUG qCDebug(lcSsl, "We're still missing %lld bytes, will check later.", missingData); #endif diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp index d7ccd9db92..d38fa299f9 100644 --- a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp +++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp @@ -48,6 +48,7 @@ #include <qscreen.h> #include <qpa/qplatformscreen.h> #include <QtCore/QUuid> +#include <QtGui/QPainterPath> #ifndef QT_NO_FREETYPE diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm index ce793dfafa..115b13d60d 100644 --- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm @@ -45,6 +45,7 @@ #include <QtCore/qsettings.h> #endif #include <QtCore/qoperatingsystemversion.h> +#include <QtGui/qpainterpath.h> #include <private/qcoregraphics_p.h> #include <private/qimage_p.h> diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp index b11ad46bc7..4e6b22ce94 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp @@ -51,6 +51,7 @@ #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatformintegration.h> #include <QtGui/private/qhighdpiscaling_p.h> +#include <QtGui/qpainterpath.h> #if defined(QT_USE_DIRECTWRITE2) # include <dwrite_2.h> diff --git a/src/platformsupport/services/genericunix/qgenericunixservices.cpp b/src/platformsupport/services/genericunix/qgenericunixservices.cpp index 734bdcaf75..b583d636c0 100644 --- a/src/platformsupport/services/genericunix/qgenericunixservices.cpp +++ b/src/platformsupport/services/genericunix/qgenericunixservices.cpp @@ -166,7 +166,12 @@ static inline bool launch(const QString &launcher, const QUrl &url) #if !QT_CONFIG(process) const bool ok = ::system(qPrintable(command + QLatin1String(" &"))); #else - const bool ok = QProcess::startDetached(command); + QStringList args = QProcess::splitCommand(command); + bool ok = false; + if (!args.isEmpty()) { + QString program = args.takeFirst(); + ok = QProcess::startDetached(program, args); + } #endif if (!ok) qWarning("Launch failed (%s)", qPrintable(command)); diff --git a/src/platformsupport/windowsuiautomation/qwindowsuiawrapper.cpp b/src/platformsupport/windowsuiautomation/qwindowsuiawrapper.cpp index 79541fe636..8038e1a3c3 100644 --- a/src/platformsupport/windowsuiautomation/qwindowsuiawrapper.cpp +++ b/src/platformsupport/windowsuiautomation/qwindowsuiawrapper.cpp @@ -53,6 +53,7 @@ QWindowsUiaWrapper::QWindowsUiaWrapper() m_pUiaHostProviderFromHwnd = reinterpret_cast<PtrUiaHostProviderFromHwnd>(uiaLib.resolve("UiaHostProviderFromHwnd")); m_pUiaRaiseAutomationPropertyChangedEvent = reinterpret_cast<PtrUiaRaiseAutomationPropertyChangedEvent>(uiaLib.resolve("UiaRaiseAutomationPropertyChangedEvent")); m_pUiaRaiseAutomationEvent = reinterpret_cast<PtrUiaRaiseAutomationEvent>(uiaLib.resolve("UiaRaiseAutomationEvent")); + m_pUiaRaiseNotificationEvent = reinterpret_cast<PtrUiaRaiseNotificationEvent>(uiaLib.resolve("UiaRaiseNotificationEvent")); m_pUiaClientsAreListening = reinterpret_cast<PtrUiaClientsAreListening>(uiaLib.resolve("UiaClientsAreListening")); } } @@ -68,7 +69,7 @@ QWindowsUiaWrapper *QWindowsUiaWrapper::instance() return &wrapper; } -// True if all symbols resolved. +// True if most symbols resolved (UiaRaiseNotificationEvent is optional). BOOL QWindowsUiaWrapper::ready() { return m_pUiaReturnRawElementProvider @@ -113,5 +114,12 @@ HRESULT QWindowsUiaWrapper::raiseAutomationEvent(IRawElementProviderSimple *pPro return m_pUiaRaiseAutomationEvent(pProvider, id); } +HRESULT QWindowsUiaWrapper::raiseNotificationEvent(IRawElementProviderSimple *provider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId) +{ + if (!m_pUiaRaiseNotificationEvent) + return UIA_E_NOTSUPPORTED; + return m_pUiaRaiseNotificationEvent(provider, notificationKind, notificationProcessing, displayString, activityId); +} + QT_END_NAMESPACE diff --git a/src/platformsupport/windowsuiautomation/qwindowsuiawrapper_p.h b/src/platformsupport/windowsuiautomation/qwindowsuiawrapper_p.h index 3ebc3008d3..9208acbc31 100644 --- a/src/platformsupport/windowsuiautomation/qwindowsuiawrapper_p.h +++ b/src/platformsupport/windowsuiautomation/qwindowsuiawrapper_p.h @@ -80,17 +80,20 @@ public: HRESULT hostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **ppProvider); HRESULT raiseAutomationPropertyChangedEvent(IRawElementProviderSimple *pProvider, PROPERTYID id, VARIANT oldValue, VARIANT newValue); HRESULT raiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id); + HRESULT raiseNotificationEvent(IRawElementProviderSimple *provider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId); private: typedef LRESULT (WINAPI *PtrUiaReturnRawElementProvider)(HWND, WPARAM, LPARAM, IRawElementProviderSimple *); typedef HRESULT (WINAPI *PtrUiaHostProviderFromHwnd)(HWND, IRawElementProviderSimple **); typedef HRESULT (WINAPI *PtrUiaRaiseAutomationPropertyChangedEvent)(IRawElementProviderSimple *, PROPERTYID, VARIANT, VARIANT); typedef HRESULT (WINAPI *PtrUiaRaiseAutomationEvent)(IRawElementProviderSimple *, EVENTID); + typedef HRESULT (WINAPI *PtrUiaRaiseNotificationEvent)(IRawElementProviderSimple *, NotificationKind, NotificationProcessing, BSTR, BSTR); typedef BOOL (WINAPI *PtrUiaClientsAreListening)(); PtrUiaReturnRawElementProvider m_pUiaReturnRawElementProvider = nullptr; PtrUiaHostProviderFromHwnd m_pUiaHostProviderFromHwnd = nullptr; PtrUiaRaiseAutomationPropertyChangedEvent m_pUiaRaiseAutomationPropertyChangedEvent = nullptr; PtrUiaRaiseAutomationEvent m_pUiaRaiseAutomationEvent = nullptr; + PtrUiaRaiseNotificationEvent m_pUiaRaiseNotificationEvent = nullptr; PtrUiaClientsAreListening m_pUiaClientsAreListening = nullptr; }; diff --git a/src/platformsupport/windowsuiautomation/uiatypes_p.h b/src/platformsupport/windowsuiautomation/uiatypes_p.h index afbc957094..0d2e1161e4 100644 --- a/src/platformsupport/windowsuiautomation/uiatypes_p.h +++ b/src/platformsupport/windowsuiautomation/uiatypes_p.h @@ -162,6 +162,22 @@ enum ExpandCollapseState { ExpandCollapseState_LeafNode = 3 }; +enum NotificationKind { + NotificationKind_ItemAdded = 0, + NotificationKind_ItemRemoved = 1, + NotificationKind_ActionCompleted = 2, + NotificationKind_ActionAborted = 3, + NotificationKind_Other = 4 +}; + +enum NotificationProcessing { + NotificationProcessing_ImportantAll = 0, + NotificationProcessing_ImportantMostRecent = 1, + NotificationProcessing_All = 2, + NotificationProcessing_MostRecent = 3, + NotificationProcessing_CurrentThenMostRecent = 4 +}; + struct UiaRect { double left; double top; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 28da4fcf5d..61bf0d4a4e 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -1632,6 +1632,7 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel) nsWindow.restorable = NO; nsWindow.level = windowLevel(flags); + nsWindow.tabbingMode = NSWindowTabbingModeDisallowed; if (shouldBePanel) { // Qt::Tool windows hide on app deactivation, unless Qt::WA_MacAlwaysShowToolWindow is set diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index 9adc5c78dd..59360616a1 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -166,11 +166,27 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve } if (event->value().type() == QVariant::String) { if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { - // Notifies changes in string values. - VARIANT oldVal, newVal; - clearVariant(&oldVal); - setVariantString(event->value().toString(), &newVal); - QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal); + + // Tries to notify the change using UiaRaiseNotificationEvent(), which is only available on + // Windows 10 version 1709 or newer. Otherwise uses UiaRaiseAutomationPropertyChangedEvent(). + + BSTR displayString = bStrFromQString(event->value().toString()); + BSTR activityId = bStrFromQString(QString()); + + HRESULT hr = QWindowsUiaWrapper::instance()->raiseNotificationEvent(provider, NotificationKind_Other, + NotificationProcessing_ImportantMostRecent, + displayString, activityId); + + ::SysFreeString(displayString); + ::SysFreeString(activityId); + + if (hr == static_cast<HRESULT>(UIA_E_NOTSUPPORTED)) { + VARIANT oldVal, newVal; + clearVariant(&oldVal); + setVariantString(event->value().toString(), &newVal); + QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal); + ::SysFreeString(newVal.bstrVal); + } } } else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) { if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { diff --git a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp index 114d6dacd8..fee8063f13 100644 --- a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp +++ b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp @@ -397,7 +397,15 @@ bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModalit RETURN_FALSE_IF_FAILED_WITH_ARGS("Failed to set default file extension \"%s\"", qPrintable(suffix)); } - const QString suggestedName = QFileInfo(d->saveFileName.toLocalFile()).fileName(); + QString suggestedName = QFileInfo(d->saveFileName.toLocalFile()).fileName(); + if (suggestedName.isEmpty() && dialogOptions->initiallySelectedFiles().size() > 0) + suggestedName = QFileInfo(dialogOptions->initiallySelectedFiles().first().toLocalFile()) + .fileName(); + if (suggestedName.isEmpty()) { + const auto fileInfo = QFileInfo(dialogOptions->initialDirectory().toLocalFile()); + if (!fileInfo.isDir()) + suggestedName = fileInfo.fileName(); + } if (!suggestedName.isEmpty()) { HStringReference nativeSuggestedName(reinterpret_cast<const wchar_t *>(suggestedName.utf16()), uint(suggestedName.length())); diff --git a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp index be1a06bc1d..2f56487f88 100644 --- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp +++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp @@ -2379,7 +2379,7 @@ QStringList QODBCDriver::tables(QSql::TableType type) const } while (r == SQL_SUCCESS) { - QString fieldVal = qGetStringData(hStmt, 2, -1, false); + QString fieldVal = qGetStringData(hStmt, 2, -1, d->unicode); tl.append(fieldVal); if (d->hasSQLFetchScroll) diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index f473c3cbe3..f8e423456f 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -56,6 +56,7 @@ #include <QtCore/private/qcore_mac_p.h> +#include <QtGui/qpainterpath.h> #include <QtGui/private/qcoregraphics_p.h> #include <QtGui/qpa/qplatformfontdatabase.h> #include <QtGui/qpa/qplatformtheme.h> diff --git a/src/printsupport/dialogs/qprintdialog_mac.mm b/src/printsupport/dialogs/qprintdialog_mac.mm index ed2d0908c4..a4101f7ec0 100644 --- a/src/printsupport/dialogs/qprintdialog_mac.mm +++ b/src/printsupport/dialogs/qprintdialog_mac.mm @@ -42,6 +42,7 @@ #include "qprintdialog.h" #include "qabstractprintdialog_p.h" +#include <QtCore/qtemporarydir.h> #include <QtCore/private/qcore_mac_p.h> #include <QtWidgets/private/qapplication_p.h> #include <QtPrintSupport/qprinter.h> @@ -127,21 +128,36 @@ QT_USE_NAMESPACE PMDestinationType dest; PMSessionGetDestinationType(session, settings, &dest); if (dest == kPMDestinationFile) { - // QTBUG-38820 - // If user selected Print to File, leave OSX to generate the PDF, - // otherwise setting PdfFormat would prevent us showing dialog again. - // TODO Restore this when QTBUG-36112 is fixed. - /* QCFType<CFURLRef> file; PMSessionCopyDestinationLocation(session, settings, &file); UInt8 localFile[2048]; // Assuming there's a POSIX file system here. CFURLGetFileSystemRepresentation(file, true, localFile, sizeof(localFile)); - printer->setOutputFileName(QString::fromUtf8(reinterpret_cast<const char *>(localFile))); - */ - } else { + auto outputFile = QFileInfo(QString::fromUtf8(reinterpret_cast<const char *>(localFile))); + if (outputFile.suffix() == QLatin1String("pdf")) + printer->setOutputFileName(outputFile.absoluteFilePath()); + else + qWarning() << "Can not print to file type" << outputFile.suffix(); + } else if (dest == kPMDestinationPreview) { + static QTemporaryDir printPreviews; + auto documentName = printer->docName(); + if (documentName.isEmpty()) + documentName = QGuiApplication::applicationDisplayName(); + auto fileName = printPreviews.filePath(QString(QLatin1String("%1.pdf")).arg(documentName)); + printer->setOutputFileName(fileName); + // Ideally we would have a callback when the PDF engine is done writing + // to the file, and open Preview in response to that. Lacking that, we + // use the quick and dirty assumption that the the print operation will + // happen synchronously after the dialog is accepted, so we can defer + // the opening of the file to the next runloop pass. + dispatch_async(dispatch_get_main_queue(), ^{ + [NSWorkspace.sharedWorkspace openFile:fileName.toNSString()]; + }); + } else if (dest == kPMDestinationProcessPDF) { + qWarning("Printing workflows are not supported"); + } else if (dest == kPMDestinationPrinter) { PMPrinter macPrinter; PMSessionGetCurrentPrinter(session, &macPrinter); - QString printerId = QString::fromCFString(PMPrinterGetID(macPrinter)); + QString printerId = QString::fromCFString(PMPrinterGetID(macPrinter)).trimmed(); if (printer->printerName() != printerId) printer->setPrinterName(printerId); } @@ -199,14 +215,18 @@ void QPrintDialogPrivate::openCocoaPrintPanel(Qt::WindowModality modality) { Q_Q(QPrintDialog); - // get the NSPrintInfo from the print engine in the platform plugin - void *voidp = 0; - (void) QMetaObject::invokeMethod(qApp->platformNativeInterface(), - "NSPrintInfoForPrintEngine", - Q_RETURN_ARG(void *, voidp), - Q_ARG(QPrintEngine *, printer->printEngine())); - printInfo = static_cast<NSPrintInfo *>(voidp); - [printInfo retain]; + if (printer->outputFormat() == QPrinter::NativeFormat) { + // get the NSPrintInfo from the print engine in the platform plugin + void *voidp = 0; + (void) QMetaObject::invokeMethod(qApp->platformNativeInterface(), + "NSPrintInfoForPrintEngine", + Q_RETURN_ARG(void *, voidp), + Q_ARG(QPrintEngine *, printer->printEngine())); + printInfo = static_cast<NSPrintInfo *>(voidp); + [printInfo retain]; + } else { + printInfo = [NSPrintInfo.sharedPrintInfo retain]; + } // It seems the only way that PM lets you use all is if the minimum // for the page range is 1. This _kind of_ makes sense if you think about @@ -269,31 +289,15 @@ void QPrintDialogPrivate::closeCocoaPrintPanel() printPanel = 0; } -static bool warnIfNotNative(QPrinter *printer) -{ - if (printer->outputFormat() != QPrinter::NativeFormat) { - qWarning("QPrintDialog: Cannot be used on non-native printers"); - return false; - } - return true; -} - - QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent) : QAbstractPrintDialog(*(new QPrintDialogPrivate), printer, parent) { - Q_D(QPrintDialog); - if (!warnIfNotNative(d->printer)) - return; setAttribute(Qt::WA_DontShowOnScreen); } QPrintDialog::QPrintDialog(QWidget *parent) : QAbstractPrintDialog(*(new QPrintDialogPrivate), 0, parent) { - Q_D(QPrintDialog); - if (!warnIfNotNative(d->printer)) - return; setAttribute(Qt::WA_DontShowOnScreen); } @@ -304,8 +308,6 @@ QPrintDialog::~QPrintDialog() int QPrintDialog::exec() { Q_D(QPrintDialog); - if (!warnIfNotNative(d->printer)) - return QDialog::Rejected; QDialog::setVisible(true); diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index bcb96841df..ba3c0dad83 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -170,6 +170,8 @@ struct Options // Versioning QString versionName; QString versionCode; + QByteArray minSdkVersion{"21"}; + QByteArray targetSdkVersion{"28"}; // lib c++ path QString stdCppPath; @@ -860,6 +862,18 @@ bool readInputFile(Options *options) } { + const QJsonValue ver = jsonObject.value(QLatin1String("android-min-sdk-version")); + if (!ver.isUndefined()) + options->minSdkVersion = ver.toString().toUtf8(); + } + + { + const QJsonValue ver = jsonObject.value(QLatin1String("android-target-sdk-version")); + if (!ver.isUndefined()) + options->targetSdkVersion = ver.toString().toUtf8(); + } + + { const QJsonObject targetArchitectures = jsonObject.value(QLatin1String("architectures")).toObject(); if (targetArchitectures.isEmpty()) { fprintf(stderr, "No target architecture defined in json file.\n"); @@ -2319,6 +2333,8 @@ bool buildAndroidProject(const Options &options) gradleProperties["buildDir"] = "build"; gradleProperties["qt5AndroidDir"] = (options.qtInstallDirectory + QLatin1String("/src/android/java")).toUtf8(); gradleProperties["androidCompileSdkVersion"] = options.androidPlatform.split(QLatin1Char('-')).last().toLocal8Bit(); + gradleProperties["qtMinSdkVersion"] = options.minSdkVersion; + gradleProperties["qtTargetSdkVersion"] = options.targetSdkVersion; if (gradleProperties["androidBuildToolsVersion"].isEmpty()) gradleProperties["androidBuildToolsVersion"] = options.sdkBuildToolsVersion.toLocal8Bit(); diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index bbcceb1ce6..b669b0fe61 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -233,6 +233,7 @@ #include <QtWidgets/qgraphicswidget.h> #include <QtGui/qpaintengine.h> #include <QtGui/qpainter.h> +#include <QtGui/qpainterpath.h> #include <QtGui/qpixmapcache.h> #include <QtGui/qpolygon.h> #include <QtGui/qtouchdevice.h> diff --git a/src/widgets/graphicsview/qgraphicssceneindex.cpp b/src/widgets/graphicsview/qgraphicssceneindex.cpp index c2e9cb4729..aab9068374 100644 --- a/src/widgets/graphicsview/qgraphicssceneindex.cpp +++ b/src/widgets/graphicsview/qgraphicssceneindex.cpp @@ -60,6 +60,7 @@ #include "qgraphicswidget.h" #include "qgraphicssceneindex_p.h" #include "qgraphicsscenebsptreeindex_p.h" +#include <QtGui/qpainterpath.h> QT_BEGIN_NAMESPACE diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp index a744847f33..1117f5e473 100644 --- a/src/widgets/graphicsview/qgraphicsview.cpp +++ b/src/widgets/graphicsview/qgraphicsview.cpp @@ -293,6 +293,7 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < #include <QtWidgets/qlayout.h> #include <QtGui/qtransform.h> #include <QtGui/qpainter.h> +#include <QtGui/qpainterpath.h> #include <QtWidgets/qscrollbar.h> #include <QtWidgets/qstyleoption.h> diff --git a/src/widgets/graphicsview/qgraphicswidget.cpp b/src/widgets/graphicsview/qgraphicswidget.cpp index 6242314e1d..472d61d437 100644 --- a/src/widgets/graphicsview/qgraphicswidget.cpp +++ b/src/widgets/graphicsview/qgraphicswidget.cpp @@ -59,6 +59,7 @@ #include <QtWidgets/qgraphicsview.h> #include <QtWidgets/qgraphicsproxywidget.h> #include <QtGui/qpalette.h> +#include <QtGui/qpainterpath.h> #include <QtWidgets/qstyleoption.h> #include <qdebug.h> diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 4b0094e578..45ac6712b4 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -6125,7 +6125,7 @@ QIcon QCommonStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption const QString cacheKey = QLatin1String("qt_mac_constructQIconFromIconRef") + QString::number(standardIcon) + QString::number(size.width()); if (standardIcon >= QStyle::SP_CustomBase) { mainIcon = theme->standardPixmap(sp, QSizeF(size)); - } else if (QPixmapCache::find(cacheKey, mainIcon) == false) { + } else if (QPixmapCache::find(cacheKey, &mainIcon) == false) { mainIcon = theme->standardPixmap(sp, QSizeF(size)); QPixmapCache::insert(cacheKey, mainIcon); } diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index 4427d69944..2a0d6d1b40 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -52,6 +52,7 @@ #include <qabstractbutton.h> #endif #include <qpainter.h> +#include <qpainterpath.h> #include <qdir.h> #include <qstyleoption.h> #include <qapplication.h> diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index 1e032b237a..b07abe4f51 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -118,6 +118,7 @@ #include <QtWidgets/qtoolbar.h> #endif +#include <QtGui/qpainterpath.h> #include <QtGui/qscreen.h> QT_BEGIN_NAMESPACE diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp index 4965d146b0..e275e3a714 100644 --- a/src/widgets/styles/qwindowsstyle.cpp +++ b/src/widgets/styles/qwindowsstyle.cpp @@ -78,6 +78,7 @@ #endif #include <private/qmath_p.h> #include <qmath.h> +#include <QtGui/qpainterpath.h> #include <QtGui/qscreen.h> #include <QtGui/qwindow.h> #include <qpa/qplatformtheme.h> diff --git a/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp b/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp index a10e706ed7..4c269822d6 100644 --- a/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp +++ b/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp @@ -158,10 +158,13 @@ private slots: QFETCH(QtMsgType, msgType); QFETCH(LoggingRuleState, result); - QLoggingRule rule(QStringRef(&pattern), true); + const auto categoryL1 = category.toLatin1(); + const auto categoryL1S = QLatin1String(categoryL1); + + QLoggingRule rule(pattern, true); LoggingRuleState state = Invalid; if (rule.flags != 0) { - switch (rule.pass(category, msgType)) { + switch (rule.pass(categoryL1S, msgType)) { case -1: QFAIL("Shoudn't happen, we set pattern to true"); break; case 0: state = NoMatch; break; case 1: state = Match; break; diff --git a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp index 5fc4343cd9..419a8138a1 100644 --- a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp +++ b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp @@ -28,12 +28,13 @@ #include <QtTest/QtTest> #include <QtGui/QBitmap> +#include <QtGui/QPainter> +#include <QtGui/QPainterPath> #include <QtGui/QPalette> -#include <QtGui/QPixmap> +#include <QtGui/QPen> #include <QtGui/QPicture> +#include <QtGui/QPixmap> #include <QtGui/QTextLength> -#include <QtGui/QPainter> -#include <QtGui/QPen> class tst_QDataStream : public QObject { diff --git a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp index bcf4e73108..35546c2968 100644 --- a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp +++ b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp @@ -73,6 +73,8 @@ QStringList toQStringList(const Iterable &i) { MAKE_ALL(QByteArray, QChar) MAKE_ALL(QByteArray, QLatin1String) +MAKE_ALL(QByteArray, char16_t) +MAKE_ALL(char16_t, QByteArray) MAKE_ALL(const char*, QChar) @@ -100,6 +102,11 @@ static QByteArray rowName(const QByteArray &data) return result; } +# define QVERIFY_NOEXCEPT(expr) do { \ + if (!has_nothrow_compare<LHS, RHS>::value) \ + QEXPECT_FAIL("", "Qt is missing a nothrow utf8-utf16 comparator", Continue); \ + QVERIFY(noexcept(expr)); } while (0) + class tst_QStringApiSymmetry : public QObject { Q_OBJECT @@ -113,9 +120,11 @@ class tst_QStringApiSymmetry : public QObject void compare_impl() const; private Q_SLOTS: - // test all combinations of {QChar, QStringRef, QString, QStringView, QLatin1String, QByteArray, const char*} + // test all combinations of {QChar, char16_t, QStringRef, QString, QStringView, QLatin1String, QByteArray, const char*} void compare_QChar_QChar_data() { compare_data(false); } void compare_QChar_QChar() { compare_impl<QChar, QChar>(); } + void compare_QChar_char16_t_data() { compare_data(false); } + void compare_QChar_char16_t() { compare_impl<QChar, char16_t>(); } void compare_QChar_QStringRef_data() { compare_data(false); } void compare_QChar_QStringRef() { compare_impl<QChar, QStringRef>(); } void compare_QChar_QString_data() { compare_data(false); } @@ -129,8 +138,27 @@ private Q_SLOTS: void compare_QChar_const_char_star_data() { compare_data(false); } void compare_QChar_const_char_star() { compare_impl<QChar, const char *>(); } + void compare_char16_t_QChar_data() { compare_data(false); } + void compare_char16_t_QChar() { compare_impl<char16_t, QChar>(); } + //void compare_char16_t_char16_t_data() { compare_data(false); } + //void compare_char16_t_char16_t() { compare_impl<char16_t, char16_t>(); } + void compare_char16_t_QStringRef_data() { compare_data(false); } + void compare_char16_t_QStringRef() { compare_impl<char16_t, QStringRef>(); } + void compare_char16_t_QString_data() { compare_data(false); } + void compare_char16_t_QString() { compare_impl<char16_t, QString>(); } + void compare_char16_t_QStringView_data() { compare_data(false); } + void compare_char16_t_QStringView() { compare_impl<char16_t, QStringView>(); } + void compare_char16_t_QLatin1String_data() { compare_data(false); } + void compare_char16_t_QLatin1String() { compare_impl<char16_t, QLatin1String>(); } + void compare_char16_t_QByteArray_data() { compare_data(false); } + void compare_char16_t_QByteArray() { compare_impl<char16_t, QByteArray>(); } + //void compare_char16_t_const_char_star_data() { compare_data(false); } + //void compare_char16_t_const_char_star() { compare_impl<char16_t, const char *>(); } + void compare_QStringRef_QChar_data() { compare_data(false); } void compare_QStringRef_QChar() { compare_impl<QStringRef, QChar>(); } + void compare_QStringRef_char16_t_data() { compare_data(false); } + void compare_QStringRef_char16_t() { compare_impl<QStringRef, char16_t>(); } void compare_QStringRef_QStringRef_data() { compare_data(); } void compare_QStringRef_QStringRef() { compare_impl<QStringRef, QStringRef>(); } void compare_QStringRef_QString_data() { compare_data(); } @@ -146,6 +174,8 @@ private Q_SLOTS: void compare_QString_QChar_data() { compare_data(false); } void compare_QString_QChar() { compare_impl<QString, QChar>(); } + void compare_QString_char16_t_data() { compare_data(false); } + void compare_QString_char16_t() { compare_impl<QString, char16_t>(); } void compare_QString_QStringRef_data() { compare_data(); } void compare_QString_QStringRef() { compare_impl<QString, QStringRef>(); } void compare_QString_QString_data() { compare_data(); } @@ -161,6 +191,8 @@ private Q_SLOTS: void compare_QStringView_QChar_data() { compare_data(false); } void compare_QStringView_QChar() { compare_impl<QStringView, QChar>(); } + void compare_QStringView_char16_t_data() { compare_data(false); } + void compare_QStringView_char16_t() { compare_impl<QStringView, char16_t>(); } void compare_QStringView_QStringRef_data() { compare_data(); } void compare_QStringView_QStringRef() { compare_impl<QStringView, QStringRef>(); } void compare_QStringView_QString_data() { compare_data(); } @@ -178,6 +210,8 @@ private Q_SLOTS: void compare_QLatin1String_QChar_data() { compare_data(false); } void compare_QLatin1String_QChar() { compare_impl<QLatin1String, QChar>(); } + void compare_QLatin1String_char16_t_data() { compare_data(false); } + void compare_QLatin1String_char16_t() { compare_impl<QLatin1String, char16_t>(); } void compare_QLatin1String_QStringRef_data() { compare_data(); } void compare_QLatin1String_QStringRef() { compare_impl<QLatin1String, QStringRef>(); } void compare_QLatin1String_QString_data() { compare_data(); } @@ -193,6 +227,8 @@ private Q_SLOTS: void compare_QByteArray_QChar_data() { compare_data(false); } void compare_QByteArray_QChar() { compare_impl<QByteArray, QChar>(); } + void compare_QByteArray_char16_t_data() { compare_data(false); } + void compare_QByteArray_char16_t() { compare_impl<QByteArray, char16_t>(); } void compare_QByteArray_QStringRef_data() { compare_data(); } void compare_QByteArray_QStringRef() { compare_impl<QByteArray, QStringRef>(); } void compare_QByteArray_QString_data() { compare_data(); } @@ -206,6 +242,8 @@ private Q_SLOTS: void compare_const_char_star_QChar_data() { compare_data(false); } void compare_const_char_star_QChar() { compare_impl<const char *, QChar>(); } + //void compare_const_char_star_char16_t_data() { compare_data(false); } + //void compare_const_char_star_char16_t() { compare_impl<const char *, char16_t>(); } void compare_const_char_star_QStringRef_data() { compare_data(); } void compare_const_char_star_QStringRef() { compare_impl<const char *, QStringRef>(); } void compare_const_char_star_QString_data() { compare_data(); } @@ -223,10 +261,12 @@ private: void member_compare_impl() const; private Q_SLOTS: - // test all combinations of {QChar, QStringRef, QString, QStringView, QLatin1String, QByteArray, const char*} + // test all combinations of {QChar, char16_t, QStringRef, QString, QStringView, QLatin1String, QByteArray, const char*} #ifdef NOT_YET_IMPLEMENTED // probably never will be - what's the point of QChar::compare(QStringView)? void member_compare_QChar_QChar_data() { member_compare_data(false); } void member_compare_QChar_QChar() { member_compare_impl<QChar, QChar>(); } + void member_compare_QChar_char16_t_data() { member_compare_data(false); } + void member_compare_QChar_char16_t() { member_compare_impl<QChar, char16_t>(); } void member_compare_QChar_QStringRef_data() { member_compare_data(false); } void member_compare_QChar_QStringRef() { member_compare_impl<QChar, QStringRef>(); } void member_compare_QChar_QString_data() { member_compare_data(false); } @@ -241,8 +281,12 @@ private Q_SLOTS: void member_compare_QChar_const_char_star() { member_compare_impl<QChar, const char *>(); } #endif + // void member_compare_char16_t_XXX() - not possible + void member_compare_QStringRef_QChar_data() { member_compare_data(false); } void member_compare_QStringRef_QChar() { member_compare_impl<QStringRef, QChar>(); } + void member_compare_QStringRef_char16_t_data() { member_compare_data(false); } + void member_compare_QStringRef_char16_t() { member_compare_impl<QStringRef, char16_t>(); } void member_compare_QStringRef_QStringRef_data() { member_compare_data(); } void member_compare_QStringRef_QStringRef() { member_compare_impl<QStringRef, QStringRef>(); } void member_compare_QStringRef_QString_data() { member_compare_data(); } @@ -262,6 +306,8 @@ private Q_SLOTS: void member_compare_QString_QChar_data() { member_compare_data(false); } void member_compare_QString_QChar() { member_compare_impl<QString, QChar>(); } + void member_compare_QString_char16_t_data() { member_compare_data(false); } + void member_compare_QString_char16_t() { member_compare_impl<QString, char16_t>(); } void member_compare_QString_QStringRef_data() { member_compare_data(); } void member_compare_QString_QStringRef() { member_compare_impl<QString, QStringRef>(); } void member_compare_QString_QString_data() { member_compare_data(); } @@ -275,26 +321,29 @@ private Q_SLOTS: void member_compare_QString_const_char_star_data() { member_compare_data(); } void member_compare_QString_const_char_star() { member_compare_impl<QString, const char *>(); } -#ifdef NOT_YET_IMPLEMENTED // QChar doesn't implicitly convert to QStringView void member_compare_QStringView_QChar_data() { member_compare_data(false); } void member_compare_QStringView_QChar() { member_compare_impl<QStringView, QChar>(); } -#endif + void member_compare_QStringView_char16_t_data() { member_compare_data(false); } + void member_compare_QStringView_char16_t() { member_compare_impl<QStringView, char16_t>(); } void member_compare_QStringView_QStringRef_data() { member_compare_data(); } void member_compare_QStringView_QStringRef() { member_compare_impl<QStringView, QStringRef>(); } void member_compare_QStringView_QString_data() { member_compare_data(); } void member_compare_QStringView_QString() { member_compare_impl<QStringView, QString>(); } void member_compare_QStringView_QStringView_data() { member_compare_data(); } void member_compare_QStringView_QStringView() { member_compare_impl<QStringView, QStringView>(); } -#ifdef NOT_YET_IMPLEMENTED void member_compare_QStringView_QLatin1String_data() { member_compare_data(); } void member_compare_QStringView_QLatin1String() { member_compare_impl<QStringView, QLatin1String>(); } +#ifdef NOT_YET_IMPLEMENTED void member_compare_QStringView_QByteArray_data() { member_compare_data(); } void member_compare_QStringView_QByteArray() { member_compare_impl<QStringView, QByteArray>(); } void member_compare_QStringView_const_char_star_data() { member_compare_data(); } void member_compare_QStringView_const_char_star() { member_compare_impl<QStringView, const char *>(); } +#endif void member_compare_QLatin1String_QChar_data() { member_compare_data(false); } void member_compare_QLatin1String_QChar() { member_compare_impl<QLatin1String, QChar>(); } + void member_compare_QLatin1String_char16_t_data() { member_compare_data(false); } + void member_compare_QLatin1String_char16_t() { member_compare_impl<QLatin1String, char16_t>(); } void member_compare_QLatin1String_QStringRef_data() { member_compare_data(); } void member_compare_QLatin1String_QStringRef() { member_compare_impl<QLatin1String, QStringRef>(); } void member_compare_QLatin1String_QString_data() { member_compare_data(); } @@ -303,6 +352,7 @@ private Q_SLOTS: void member_compare_QLatin1String_QStringView() { member_compare_impl<QLatin1String, QStringView>(); } void member_compare_QLatin1String_QLatin1String_data() { member_compare_data(); } void member_compare_QLatin1String_QLatin1String() { member_compare_impl<QLatin1String, QLatin1String>(); } +#ifdef NOT_YET_IMPLEMENTED void member_compare_QLatin1String_QByteArray_data() { member_compare_data(); } void member_compare_QLatin1String_QByteArray() { member_compare_impl<QLatin1String, QByteArray>(); } void member_compare_QLatin1String_const_char_star_data() { member_compare_data(); } @@ -310,6 +360,8 @@ private Q_SLOTS: void member_compare_QByteArray_QChar_data() { member_compare_data(false); } void member_compare_QByteArray_QChar() { member_compare_impl<QByteArray, QChar>(); } + void member_compare_QByteArray_char16_t_data() { member_compare_data(false); } + void member_compare_QByteArray_char16_t() { member_compare_impl<QByteArray, char16_t>(); } void member_compare_QByteArray_QStringRef_data() { member_compare_data(); } void member_compare_QByteArray_QStringRef() { member_compare_impl<QByteArray, QStringRef>(); } void member_compare_QByteArray_QString_data() { member_compare_data(); } @@ -330,7 +382,7 @@ private: template <typename Haystack, typename Needle> void endsWith_impl() const; private Q_SLOTS: - // test all combinations of {QString, QStringRef, QStringView, QLatin1String} x {QString, QStringRef, QStringView, QLatin1String, QChar}: + // test all combinations of {QString, QStringRef, QStringView, QLatin1String} x {QString, QStringRef, QStringView, QLatin1String, QChar, char16_t}: void startsWith_QString_QString_data() { startsWith_data(); } void startsWith_QString_QString() { startsWith_impl<QString, QString>(); } void startsWith_QString_QStringRef_data() { startsWith_data(); } @@ -341,6 +393,8 @@ private Q_SLOTS: void startsWith_QString_QLatin1String() { startsWith_impl<QString, QLatin1String>(); } void startsWith_QString_QChar_data() { startsWith_data(false); } void startsWith_QString_QChar() { startsWith_impl<QString, QChar>(); } + void startsWith_QString_char16_t_data() { startsWith_data(false); } + void startsWith_QString_char16_t() { startsWith_impl<QString, char16_t>(); } void startsWith_QStringRef_QString_data() { startsWith_data(); } void startsWith_QStringRef_QString() { startsWith_impl<QStringRef, QString>(); } @@ -352,6 +406,8 @@ private Q_SLOTS: void startsWith_QStringRef_QLatin1String() { startsWith_impl<QStringRef, QLatin1String>(); } void startsWith_QStringRef_QChar_data() { startsWith_data(false); } void startsWith_QStringRef_QChar() { startsWith_impl<QStringRef, QChar>(); } + void startsWith_QStringRef_char16_t_data() { startsWith_data(false); } + void startsWith_QStringRef_char16_t() { startsWith_impl<QStringRef, char16_t>(); } void startsWith_QStringView_QString_data() { startsWith_data(); } void startsWith_QStringView_QString() { startsWith_impl<QStringView, QString>(); } @@ -363,6 +419,8 @@ private Q_SLOTS: void startsWith_QStringView_QLatin1String() { startsWith_impl<QStringView, QLatin1String>(); } void startsWith_QStringView_QChar_data() { startsWith_data(false); } void startsWith_QStringView_QChar() { startsWith_impl<QStringView, QChar>(); } + void startsWith_QStringView_char16_t_data() { startsWith_data(false); } + void startsWith_QStringView_char16_t() { startsWith_impl<QStringView, char16_t>(); } void startsWith_QLatin1String_QString_data() { startsWith_data(); } void startsWith_QLatin1String_QString() { startsWith_impl<QLatin1String, QString>(); } @@ -374,6 +432,8 @@ private Q_SLOTS: void startsWith_QLatin1String_QLatin1String() { startsWith_impl<QLatin1String, QLatin1String>(); } void startsWith_QLatin1String_QChar_data() { startsWith_data(false); } void startsWith_QLatin1String_QChar() { startsWith_impl<QLatin1String, QChar>(); } + void startsWith_QLatin1String_char16_t_data() { startsWith_data(false); } + void startsWith_QLatin1String_char16_t() { startsWith_impl<QLatin1String, char16_t>(); } void endsWith_QString_QString_data() { endsWith_data(); } void endsWith_QString_QString() { endsWith_impl<QString, QString>(); } @@ -385,6 +445,8 @@ private Q_SLOTS: void endsWith_QString_QLatin1String() { endsWith_impl<QString, QLatin1String>(); } void endsWith_QString_QChar_data() { endsWith_data(false); } void endsWith_QString_QChar() { endsWith_impl<QString, QChar>(); } + void endsWith_QString_char16_t_data() { endsWith_data(false); } + void endsWith_QString_char16_t() { endsWith_impl<QString, char16_t>(); } void endsWith_QStringRef_QString_data() { endsWith_data(); } void endsWith_QStringRef_QString() { endsWith_impl<QStringRef, QString>(); } @@ -396,6 +458,8 @@ private Q_SLOTS: void endsWith_QStringRef_QLatin1String() { endsWith_impl<QStringRef, QLatin1String>(); } void endsWith_QStringRef_QChar_data() { endsWith_data(false); } void endsWith_QStringRef_QChar() { endsWith_impl<QStringRef, QChar>(); } + void endsWith_QStringRef_char16_t_data() { endsWith_data(false); } + void endsWith_QStringRef_char16_t() { endsWith_impl<QStringRef, char16_t>(); } void endsWith_QStringView_QString_data() { endsWith_data(); } void endsWith_QStringView_QString() { endsWith_impl<QStringView, QString>(); } @@ -407,6 +471,8 @@ private Q_SLOTS: void endsWith_QStringView_QLatin1String() { endsWith_impl<QStringView, QLatin1String>(); } void endsWith_QStringView_QChar_data() { endsWith_data(false); } void endsWith_QStringView_QChar() { endsWith_impl<QStringView, QChar>(); } + void endsWith_QStringView_char16_t_data() { endsWith_data(false); } + void endsWith_QStringView_char16_t() { endsWith_impl<QStringView, char16_t>(); } void endsWith_QLatin1String_QString_data() { endsWith_data(); } void endsWith_QLatin1String_QString() { endsWith_impl<QLatin1String, QString>(); } @@ -418,6 +484,8 @@ private Q_SLOTS: void endsWith_QLatin1String_QLatin1String() { endsWith_impl<QLatin1String, QLatin1String>(); } void endsWith_QLatin1String_QChar_data() { endsWith_data(false); } void endsWith_QLatin1String_QChar() { endsWith_impl<QLatin1String, QChar>(); } + void endsWith_QLatin1String_char16_t_data() { endsWith_data(false); } + void endsWith_QLatin1String_char16_t() { endsWith_impl<QLatin1String, char16_t>(); } private: void split_data(bool rhsHasVariableLength = true); @@ -566,9 +634,10 @@ private Q_SLOTS: private: template <typename Haystack, typename Needle> void indexOf_impl() const; - void indexOf_data(); + void indexOf_data(bool rhsHasVariableLength = true); private Q_SLOTS: + // test all combinations of {QString, QLatin1String, QStringRef, QStringView} x {QString, QLatin1String, QStringRef, QStringView, QChar, char16_t}: void indexOf_QString_QString_data() { indexOf_data(); } void indexOf_QString_QString() { indexOf_impl<QString, QString>(); } void indexOf_QString_QLatin1String_data() { indexOf_data(); } @@ -577,6 +646,10 @@ private Q_SLOTS: void indexOf_QString_QStringRef() { indexOf_impl<QString, QStringRef>(); } void indexOf_QString_QStringView_data() { indexOf_data(); } void indexOf_QString_QStringView() { indexOf_impl<QString, QStringView>(); } + void indexOf_QString_QChar_data() { indexOf_data(false); } + void indexOf_QString_QChar() { indexOf_impl<QString, QChar>(); } + void indexOf_QString_char16_t_data() { indexOf_data(false); } + void indexOf_QString_char16_t() { indexOf_impl<QString, char16_t>(); } void indexOf_QLatin1String_QString_data() { indexOf_data(); } void indexOf_QLatin1String_QString() { indexOf_impl<QLatin1String, QString>(); } @@ -586,6 +659,10 @@ private Q_SLOTS: void indexOf_QLatin1String_QStringRef() { indexOf_impl<QLatin1String, QStringRef>(); } void indexOf_QLatin1String_QStringView_data() { indexOf_data(); } void indexOf_QLatin1String_QStringView() { indexOf_impl<QLatin1String, QStringView>(); } + void indexOf_QLatin1String_QChar_data() { indexOf_data(false); } + void indexOf_QLatin1String_QChar() { indexOf_impl<QLatin1String, QChar>(); } + void indexOf_QLatin1String_char16_t_data() { indexOf_data(false); } + void indexOf_QLatin1String_char16_t() { indexOf_impl<QLatin1String, char16_t>(); } void indexOf_QStringRef_QString_data() { indexOf_data(); } void indexOf_QStringRef_QString() { indexOf_impl<QStringRef, QString>(); } @@ -595,6 +672,10 @@ private Q_SLOTS: void indexOf_QStringRef_QStringRef() { indexOf_impl<QStringRef, QStringRef>(); } void indexOf_QStringRef_QStringView_data() { indexOf_data(); } void indexOf_QStringRef_QStringView() { indexOf_impl<QStringRef, QStringView>(); } + void indexOf_QStringRef_QChar_data() { indexOf_data(false); } + void indexOf_QStringRef_QChar() { indexOf_impl<QStringRef, QChar>(); } + void indexOf_QStringRef_char16_t_data() { indexOf_data(false); } + void indexOf_QStringRef_char16_t() { indexOf_impl<QStringRef, char16_t>(); } void indexOf_QStringView_QString_data() { indexOf_data(); } void indexOf_QStringView_QString() { indexOf_impl<QStringView, QString>(); } @@ -604,12 +685,17 @@ private Q_SLOTS: void indexOf_QStringView_QStringRef() { indexOf_impl<QStringView, QStringRef>(); } void indexOf_QStringView_QStringView_data() { indexOf_data(); } void indexOf_QStringView_QStringView() { indexOf_impl<QStringView, QStringView>(); } + void indexOf_QStringView_QChar_data() { indexOf_data(false); } + void indexOf_QStringView_QChar() { indexOf_impl<QStringView, QChar>(); } + void indexOf_QStringView_char16_t_data() { indexOf_data(false); } + void indexOf_QStringView_char16_t() { indexOf_impl<QStringView, char16_t>(); } private: template <typename Haystack, typename Needle> void contains_impl() const; - void contains_data(); + void contains_data(bool rhsHasVariableLength = true); private Q_SLOTS: + // test all combinations of {QString, QLatin1String, QStringRef, QStringView} x {QString, QLatin1String, QStringRef, QStringView, QChar, char16_t}: void contains_QString_QString_data() { contains_data(); } void contains_QString_QString() { contains_impl<QString, QString>(); } void contains_QString_QLatin1String_data() { contains_data(); } @@ -618,6 +704,10 @@ private Q_SLOTS: void contains_QString_QStringRef() { contains_impl<QString, QStringRef>(); } void contains_QString_QStringView_data() { contains_data(); } void contains_QString_QStringView() { contains_impl<QString, QStringView>(); } + void contains_QString_QChar_data() { contains_data(false); } + void contains_QString_QChar() { contains_impl<QString, QChar>(); } + void contains_QString_char16_t_data() { contains_data(false); } + void contains_QString_char16_t() { contains_impl<QString, char16_t>(); } void contains_QLatin1String_QString_data() { contains_data(); } void contains_QLatin1String_QString() { contains_impl<QLatin1String, QString>(); } @@ -627,6 +717,10 @@ private Q_SLOTS: void contains_QLatin1String_QStringRef() { contains_impl<QLatin1String, QStringRef>(); } void contains_QLatin1String_QStringView_data() { contains_data(); } void contains_QLatin1String_QStringView() { contains_impl<QLatin1String, QStringView>(); } + void contains_QLatin1String_QChar_data() { contains_data(false); } + void contains_QLatin1String_QChar() { contains_impl<QLatin1String, QChar>(); } + void contains_QLatin1String_char16_t_data() { contains_data(false); } + void contains_QLatin1String_char16_t() { contains_impl<QLatin1String, char16_t>(); } void contains_QStringRef_QString_data() { contains_data(); } void contains_QStringRef_QString() { contains_impl<QStringRef, QString>(); } @@ -636,6 +730,10 @@ private Q_SLOTS: void contains_QStringRef_QStringRef() { contains_impl<QStringRef, QStringRef>(); } void contains_QStringRef_QStringView_data() { contains_data(); } void contains_QStringRef_QStringView() { contains_impl<QStringRef, QStringView>(); } + void contains_QStringRef_QChar_data() { contains_data(false); } + void contains_QStringRef_QChar() { contains_impl<QStringRef, QChar>(); } + void contains_QStringRef_char16_t_data() { contains_data(false); } + void contains_QStringRef_char16_t() { contains_impl<QStringRef, char16_t>(); } void contains_QStringView_QString_data() { contains_data(); } void contains_QStringView_QString() { contains_impl<QStringView, QString>(); } @@ -645,12 +743,17 @@ private Q_SLOTS: void contains_QStringView_QStringRef() { contains_impl<QStringView, QStringRef>(); } void contains_QStringView_QStringView_data() { contains_data(); } void contains_QStringView_QStringView() { contains_impl<QStringView, QStringView>(); } + void contains_QStringView_QChar_data() { contains_data(false); } + void contains_QStringView_QChar() { contains_impl<QStringView, QChar>(); } + void contains_QStringView_char16_t_data() { contains_data(false); } + void contains_QStringView_char16_t() { contains_impl<QStringView, char16_t>(); } private: template <typename Haystack, typename Needle> void lastIndexOf_impl() const; - void lastIndexOf_data(); + void lastIndexOf_data(bool rhsHasVariableLength = true); private Q_SLOTS: + // test all combinations of {QString, QLatin1String, QStringRef, QStringView} x {QString, QLatin1String, QStringRef, QStringView, QChar, char16_t}: void lastIndexOf_QString_QString_data() { lastIndexOf_data(); } void lastIndexOf_QString_QString() { lastIndexOf_impl<QString, QString>(); } void lastIndexOf_QString_QLatin1String_data() { lastIndexOf_data(); } @@ -659,6 +762,10 @@ private Q_SLOTS: void lastIndexOf_QString_QStringRef() { lastIndexOf_impl<QString, QStringRef>(); } void lastIndexOf_QString_QStringView_data() { lastIndexOf_data(); } void lastIndexOf_QString_QStringView() { lastIndexOf_impl<QString, QStringView>(); } + void lastIndexOf_QString_QChar_data() { lastIndexOf_data(false); } + void lastIndexOf_QString_QChar() { lastIndexOf_impl<QString, QChar>(); } + void lastIndexOf_QString_char16_t_data() { lastIndexOf_data(false); } + void lastIndexOf_QString_char16_t() { lastIndexOf_impl<QString, char16_t>(); } void lastIndexOf_QLatin1String_QString_data() { lastIndexOf_data(); } void lastIndexOf_QLatin1String_QString() { lastIndexOf_impl<QLatin1String, QString>(); } @@ -668,6 +775,10 @@ private Q_SLOTS: void lastIndexOf_QLatin1String_QStringRef() { lastIndexOf_impl<QLatin1String, QStringRef>(); } void lastIndexOf_QLatin1String_QStringView_data() { lastIndexOf_data(); } void lastIndexOf_QLatin1String_QStringView() { lastIndexOf_impl<QLatin1String, QStringView>(); } + void lastIndexOf_QLatin1String_QChar_data() { lastIndexOf_data(false); } + void lastIndexOf_QLatin1String_QChar() { lastIndexOf_impl<QLatin1String, QChar>(); } + void lastIndexOf_QLatin1String_char16_t_data() { lastIndexOf_data(false); } + void lastIndexOf_QLatin1String_char16_t() { lastIndexOf_impl<QLatin1String, char16_t>(); } void lastIndexOf_QStringRef_QString_data() { lastIndexOf_data(); } void lastIndexOf_QStringRef_QString() { lastIndexOf_impl<QStringRef, QString>(); } @@ -677,6 +788,10 @@ private Q_SLOTS: void lastIndexOf_QStringRef_QStringRef() { lastIndexOf_impl<QStringRef, QStringRef>(); } void lastIndexOf_QStringRef_QStringView_data() { lastIndexOf_data(); } void lastIndexOf_QStringRef_QStringView() { lastIndexOf_impl<QStringRef, QStringView>(); } + void lastIndexOf_QStringRef_QChar_data() { lastIndexOf_data(false); } + void lastIndexOf_QStringRef_QChar() { lastIndexOf_impl<QStringRef, QChar>(); } + void lastIndexOf_QStringRef_char16_t_data() { lastIndexOf_data(false); } + void lastIndexOf_QStringRef_char16_t() { lastIndexOf_impl<QStringRef, char16_t>(); } void lastIndexOf_QStringView_QString_data() { lastIndexOf_data(); } void lastIndexOf_QStringView_QString() { lastIndexOf_impl<QStringView, QString>(); } @@ -686,6 +801,10 @@ private Q_SLOTS: void lastIndexOf_QStringView_QStringRef() { lastIndexOf_impl<QStringView, QStringRef>(); } void lastIndexOf_QStringView_QStringView_data() { lastIndexOf_data(); } void lastIndexOf_QStringView_QStringView() { lastIndexOf_impl<QStringView, QStringView>(); } + void lastIndexOf_QStringView_QChar_data() { lastIndexOf_data(false); } + void lastIndexOf_QStringView_QChar() { lastIndexOf_impl<QStringView, QChar>(); } + void lastIndexOf_QStringView_char16_t_data() { lastIndexOf_data(false); } + void lastIndexOf_QStringView_char16_t() { lastIndexOf_impl<QStringView, char16_t>(); } }; void tst_QStringApiSymmetry::compare_data(bool hasConceptOfNullAndEmpty) @@ -725,6 +844,8 @@ void tst_QStringApiSymmetry::compare_data(bool hasConceptOfNullAndEmpty) ROW("0", ""); ROW("0", "1"); ROW("0", "0"); + ROW("10", "0"); + ROW("01", "1"); ROW("\xE4", "\xE4"); // ä <> ä ROW("\xE4", "\xC4"); // ä <> Ä #undef ROW @@ -741,6 +862,7 @@ template <typename String> String detached(String s) template <class Str> Str make(const QStringRef &sf, QLatin1String l1, const QByteArray &u8); template <> QChar make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf.isEmpty() ? QChar() : sf.at(0); } +template <> char16_t make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf.isEmpty() ? char16_t() : char16_t{sf.at(0).unicode()}; } template <> QStringRef make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf; } template <> QString make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf.toString(); } template <> QStringView make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf; } @@ -783,15 +905,6 @@ void tst_QStringApiSymmetry::compare_impl() const const auto lhs = make<LHS>(lhsUnicode, lhsLatin1, lhsU8); const auto rhs = make<RHS>(rhsUnicode, rhsLatin1, rhsU8); -#ifdef Q_COMPILER_NOEXCEPT -# define QVERIFY_NOEXCEPT(expr) do { \ - if (has_nothrow_compare<LHS, RHS>::value) {} else \ - QEXPECT_FAIL("", "Qt is missing a nothrow utf8-utf16 comparator", Continue); \ - QVERIFY(noexcept(expr)); } while (0) -#else -# define QVERIFY_NOEXCEPT(expr) -#endif - #define CHECK(op) \ QVERIFY_NOEXCEPT(lhs op rhs); \ do { if (caseSensitiveCompareResult op 0) { \ @@ -825,15 +938,6 @@ void tst_QStringApiSymmetry::member_compare_impl() const const auto lhs = make<LHS>(lhsUnicode, lhsLatin1, lhsU8); const auto rhs = make<RHS>(rhsUnicode, rhsLatin1, rhsU8); -#define QVERIFY_NOEXCEPT(expr) do { \ - if (has_nothrow_compare<LHS, RHS>::value) {} else \ - QEXPECT_FAIL("", "Qt is missing a nothrow utf8-utf16 comparator", Continue); \ - QVERIFY(noexcept(expr)); } while (0) - - if (std::is_same<LHS, QByteArray>::value || // needs to simply be marked as noexcept - ((std::is_same<LHS, QString>::value || std::is_same<LHS, QStringRef>::value) - && std::is_same<RHS, QChar>::value)) // implict QChar -> QString conversion kills noexcept - QEXPECT_FAIL("", "known issues, will be fixed before 5.14 release", Continue); QVERIFY_NOEXCEPT(lhs.compare(rhs, Qt::CaseSensitive)); QCOMPARE(sign(lhs.compare(rhs)), caseSensitiveCompareResult); @@ -1646,7 +1750,7 @@ void tst_QStringApiSymmetry::toUcs4_impl() QCOMPARE(unicode.isEmpty(), ucs4.isEmpty()); } -void tst_QStringApiSymmetry::indexOf_data() +void tst_QStringApiSymmetry::indexOf_data(bool rhsHasVariableLength) { QTest::addColumn<QString>("haystackU16"); QTest::addColumn<QLatin1String>("haystackL1"); @@ -1659,18 +1763,20 @@ void tst_QStringApiSymmetry::indexOf_data() constexpr qsizetype zeroPos = 0; constexpr qsizetype minus1Pos = -1; - QTest::addRow("haystack: null, needle: null") << null << QLatin1String() + if (rhsHasVariableLength) { + QTest::addRow("haystack: null, needle: null") << null << QLatin1String() << null << QLatin1String() << zeroPos << zeroPos << zeroPos; - QTest::addRow("haystack: empty, needle: null") << empty << QLatin1String("") + QTest::addRow("haystack: empty, needle: null") << empty << QLatin1String("") << null << QLatin1String() << zeroPos << zeroPos << zeroPos; - QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") + QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") << null << QLatin1String() << zeroPos << zeroPos << zeroPos; - QTest::addRow("haystack: null, needle: empty") << null << QLatin1String() + QTest::addRow("haystack: null, needle: empty") << null << QLatin1String() << empty << QLatin1String("") << zeroPos << zeroPos << zeroPos; - QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") + QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") << empty << QLatin1String("") << zeroPos << zeroPos << zeroPos; - QTest::addRow("haystack: empty, needle: empty") << empty << QLatin1String("") + QTest::addRow("haystack: empty, needle: empty") << empty << QLatin1String("") << empty << QLatin1String("") << zeroPos << zeroPos << zeroPos; + } QTest::addRow("haystack: empty, needle: a") << empty << QLatin1String("") << a << QLatin1String("a") << zeroPos << minus1Pos << minus1Pos; QTest::addRow("haystack: null, needle: a") << null << QLatin1String() @@ -1702,17 +1808,19 @@ void tst_QStringApiSymmetry::indexOf_data() ROW(ABC, b, 1, -1, 1); ROW(ABC, B, 2, -1, -1); - ROW(aBc, bc, 0, -1, 1); - ROW(aBc, Bc, 0, 1, 1); - ROW(aBc, bC, 0, -1, 1); - ROW(aBc, BC, 0, -1, 1); - - ROW(AbC, bc, 0, -1, 1); - ROW(AbC, Bc, 0, -1, 1); - ROW(AbC, bC, 0, 1, 1); - ROW(AbC, BC, 0, -1, 1); - ROW(AbC, BC, 1, -1, 1); - ROW(AbC, BC, 2, -1, -1); + if (rhsHasVariableLength) { + ROW(aBc, bc, 0, -1, 1); + ROW(aBc, Bc, 0, 1, 1); + ROW(aBc, bC, 0, -1, 1); + ROW(aBc, BC, 0, -1, 1); + + ROW(AbC, bc, 0, -1, 1); + ROW(AbC, Bc, 0, -1, 1); + ROW(AbC, bC, 0, 1, 1); + ROW(AbC, BC, 0, -1, 1); + ROW(AbC, BC, 1, -1, 1); + ROW(AbC, BC, 2, -1, -1); + } #undef ROW } @@ -1739,13 +1847,6 @@ void tst_QStringApiSymmetry::indexOf_impl() const QCOMPARE(haystack.indexOf(needle, startpos), size_type(resultCS)); QCOMPARE(haystack.indexOf(needle, startpos, Qt::CaseSensitive), size_type(resultCS)); QCOMPARE(haystack.indexOf(needle, startpos, Qt::CaseInsensitive), size_type(resultCIS)); - - if (needle.size() == 1) - { - QCOMPARE(haystack.indexOf(needle[0], startpos), size_type(resultCS)); - QCOMPARE(haystack.indexOf(needle[0], startpos, Qt::CaseSensitive), size_type(resultCS)); - QCOMPARE(haystack.indexOf(needle[0], startpos, Qt::CaseInsensitive), size_type(resultCIS)); - } } static QString ABCDEFGHIEfGEFG = QStringLiteral("ABCDEFGHIEfGEFG"); @@ -1755,7 +1856,7 @@ static QString asd = QStringLiteral("asd"); static QString asdf = QStringLiteral("asdf"); static QString Z = QStringLiteral("Z"); -void tst_QStringApiSymmetry::contains_data() +void tst_QStringApiSymmetry::contains_data(bool rhsHasVariableLength) { QTest::addColumn<QString>("haystackU16"); QTest::addColumn<QLatin1String>("haystackL1"); @@ -1764,18 +1865,20 @@ void tst_QStringApiSymmetry::contains_data() QTest::addColumn<bool>("resultCS"); QTest::addColumn<bool>("resultCIS"); - QTest::addRow("haystack: null, needle: null") << null << QLatin1String() + if (rhsHasVariableLength) { + QTest::addRow("haystack: null, needle: null") << null << QLatin1String() << null << QLatin1String() << true << true; - QTest::addRow("haystack: empty, needle: null") << empty << QLatin1String("") + QTest::addRow("haystack: empty, needle: null") << empty << QLatin1String("") << null << QLatin1String() << true << true; - QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") + QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") << null << QLatin1String() << true << true; - QTest::addRow("haystack: null, needle: empty") << null << QLatin1String() + QTest::addRow("haystack: null, needle: empty") << null << QLatin1String() << empty << QLatin1String("") << true << true; - QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") + QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") << empty << QLatin1String("") << true << true;; - QTest::addRow("haystack: empty, needle: empty") << empty << QLatin1String("") + QTest::addRow("haystack: empty, needle: empty") << empty << QLatin1String("") << empty << QLatin1String("") << true << true; + } QTest::addRow("haystack: empty, needle: a") << empty << QLatin1String("") << a << QLatin1String("a") << false << false; QTest::addRow("haystack: null, needle: a") << null << QLatin1String() @@ -1789,8 +1892,10 @@ void tst_QStringApiSymmetry::contains_data() ROW(ABCDEFGHIEfGEFG, A, true, true); ROW(ABCDEFGHIEfGEFG, a, false, true); ROW(ABCDEFGHIEfGEFG, Z, false, false); - ROW(ABCDEFGHIEfGEFG, EFG, true, true); - ROW(ABCDEFGHIEfGEFG, efg, false, true); + if (rhsHasVariableLength) { + ROW(ABCDEFGHIEfGEFG, EFG, true, true); + ROW(ABCDEFGHIEfGEFG, efg, false, true); + } ROW(ABCDEFGHIEfGEFG, E, true, true); ROW(ABCDEFGHIEfGEFG, e, false, true); #undef ROW @@ -1815,16 +1920,9 @@ void tst_QStringApiSymmetry::contains_impl() const QCOMPARE(haystack.contains(needle), resultCS); QCOMPARE(haystack.contains(needle, Qt::CaseSensitive), resultCS); QCOMPARE(haystack.contains(needle, Qt::CaseInsensitive), resultCIS); - - if (needle.size() == 1) - { - QCOMPARE(haystack.contains(needle[0]), resultCS); - QCOMPARE(haystack.contains(needle[0], Qt::CaseSensitive), resultCS); - QCOMPARE(haystack.contains(needle[0], Qt::CaseInsensitive), resultCIS); - } } -void tst_QStringApiSymmetry::lastIndexOf_data() +void tst_QStringApiSymmetry::lastIndexOf_data(bool rhsHasVariableLength) { QTest::addColumn<QString>("haystackU16"); QTest::addColumn<QLatin1String>("haystackL1"); @@ -1837,38 +1935,43 @@ void tst_QStringApiSymmetry::lastIndexOf_data() constexpr qsizetype zeroPos = 0; constexpr qsizetype minus1Pos = -1; - QTest::addRow("haystack: null, needle: null") << null << QLatin1String() + if (rhsHasVariableLength) { + QTest::addRow("haystack: null, needle: null") << null << QLatin1String() << null << QLatin1String() << minus1Pos << minus1Pos << minus1Pos; - QTest::addRow("haystack: empty, needle: null") << empty << QLatin1String("") + QTest::addRow("haystack: empty, needle: null") << empty << QLatin1String("") << null << QLatin1String() << minus1Pos << minus1Pos << minus1Pos; - QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") + QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") << null << QLatin1String() << minus1Pos << zeroPos << zeroPos; - QTest::addRow("haystack: null, needle: empty") << null << QLatin1String() + QTest::addRow("haystack: null, needle: empty") << null << QLatin1String() << empty << QLatin1String("") << minus1Pos << minus1Pos << minus1Pos; - QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") + QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") << empty << QLatin1String("") << minus1Pos << zeroPos << zeroPos; - QTest::addRow("haystack: empty, needle: empty") << empty << QLatin1String("") + QTest::addRow("haystack: empty, needle: empty") << empty << QLatin1String("") << empty << QLatin1String("") << minus1Pos << minus1Pos << minus1Pos; + } QTest::addRow("haystack: empty, needle: a") << empty << QLatin1String("") << a << QLatin1String("a") << minus1Pos << minus1Pos << minus1Pos; QTest::addRow("haystack: null, needle: a") << null << QLatin1String() << a << QLatin1String("a") << minus1Pos << minus1Pos << minus1Pos; - QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") + if (rhsHasVariableLength) { + QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") << null << QLatin1String() << qsizetype(1) << qsizetype(1) << qsizetype(1); - QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") + QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") << empty << QLatin1String("") << qsizetype(1) << qsizetype(1) << qsizetype(1); - QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") + QTest::addRow("haystack: a, needle: null") << a << QLatin1String("a") << null << QLatin1String() << qsizetype(2) << minus1Pos << minus1Pos; - QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") + QTest::addRow("haystack: a, needle: empty") << a << QLatin1String("a") << empty << QLatin1String("") << qsizetype(2) << minus1Pos << minus1Pos; + } #define ROW(h, n, st, cs, cis) \ QTest::addRow("haystack: %s, needle: %s", #h, #n) << h << QLatin1String(#h) \ << n << QLatin1String(#n) \ << qsizetype(st) << qsizetype(cs) << qsizetype(cis) - ROW(asd, asdf, -1, -1, -1); + if (rhsHasVariableLength) + ROW(asd, asdf, -1, -1, -1); ROW(ABCDEFGHIEfGEFG, G, -1, 14, 14); ROW(ABCDEFGHIEfGEFG, g, -1, -1, 14); @@ -1895,13 +1998,15 @@ void tst_QStringApiSymmetry::lastIndexOf_data() ROW(ABCDEFGHIEfGEFG, A, -15, 0, 0); ROW(ABCDEFGHIEfGEFG, a, -15, -1, 0); - ROW(ABCDEFGHIEfGEFG, efg, 0, -1, -1); - ROW(ABCDEFGHIEfGEFG, efg, 15, -1, -1); - ROW(ABCDEFGHIEfGEFG, efg, -15, -1, -1); - ROW(ABCDEFGHIEfGEFG, efg, 14, -1, 12); - ROW(ABCDEFGHIEfGEFG, efg, 12, -1, 12); - ROW(ABCDEFGHIEfGEFG, efg, -12, -1, -1); - ROW(ABCDEFGHIEfGEFG, efg, 11, -1, 9); + if (rhsHasVariableLength) { + ROW(ABCDEFGHIEfGEFG, efg, 0, -1, -1); + ROW(ABCDEFGHIEfGEFG, efg, 15, -1, -1); + ROW(ABCDEFGHIEfGEFG, efg, -15, -1, -1); + ROW(ABCDEFGHIEfGEFG, efg, 14, -1, 12); + ROW(ABCDEFGHIEfGEFG, efg, 12, -1, 12); + ROW(ABCDEFGHIEfGEFG, efg, -12, -1, -1); + ROW(ABCDEFGHIEfGEFG, efg, 11, -1, 9); + } #undef ROW } @@ -1928,12 +2033,6 @@ void tst_QStringApiSymmetry::lastIndexOf_impl() const QCOMPARE(haystack.lastIndexOf(needle, startpos, Qt::CaseSensitive), size_type(resultCS)); QCOMPARE(haystack.lastIndexOf(needle, startpos, Qt::CaseInsensitive), size_type(resultCIS)); - if (needle.size() == 1) - { - QCOMPARE(haystack.lastIndexOf(needle[0], startpos), size_type(resultCS)); - QCOMPARE(haystack.lastIndexOf(needle[0], startpos, Qt::CaseSensitive), size_type(resultCS)); - QCOMPARE(haystack.lastIndexOf(needle[0], startpos, Qt::CaseInsensitive), size_type(resultCIS)); - } } QTEST_APPLESS_MAIN(tst_QStringApiSymmetry) diff --git a/tests/auto/corelib/tools/qlinkedlist/tst_qlinkedlist.cpp b/tests/auto/corelib/tools/qlinkedlist/tst_qlinkedlist.cpp new file mode 100644 index 0000000000..b681676d09 --- /dev/null +++ b/tests/auto/corelib/tools/qlinkedlist/tst_qlinkedlist.cpp @@ -0,0 +1,1148 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QLinkedList> + +#if QT_DEPRECATED_SINCE(5, 15) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + +struct Movable +{ + Movable(char input = 'j') : i(input), state(Constructed) + { + ++liveCount; + } + Movable(const Movable &other) + : i(other.i) + , state(Constructed) + { + check(other.state, Constructed); + ++liveCount; + } + + ~Movable() + { + check(state, Constructed); + i = 0; + --liveCount; + state = Destructed; + } + + bool operator ==(const Movable &other) const + { + check(state, Constructed); + check(other.state, Constructed); + return i == other.i; + } + + Movable &operator=(const Movable &other) + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + return *this; + } + char i; + + static int getLiveCount() { return liveCount; } +private: + static int liveCount; + + enum State { Constructed = 106, Destructed = 110 }; + State state; + + static void check(const State state1, const State state2) + { + QCOMPARE(int(state1), int(state2)); + } +}; + +int Movable::liveCount = 0; + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Movable); + +Q_DECLARE_METATYPE(QLinkedList<int>); + + +int qHash(const Movable& movable) +{ + return qHash(movable.i); +} + +struct Complex +{ + Complex(int val = 0) + : value(val) + , checkSum(this) + { + ++liveCount; + } + + Complex(Complex const &other) + : value(other.value) + , checkSum(this) + { + ++liveCount; + } + + Complex &operator=(Complex const &other) + { + check(); other.check(); + + value = other.value; + return *this; + } + + ~Complex() + { + --liveCount; + check(); + } + + operator int() const { return value; } + + bool operator==(Complex const &other) const + { + check(); other.check(); + return value == other.value; + } + + void check() const + { + QVERIFY(this == checkSum); + } + + static int getLiveCount() { return liveCount; } +private: + static int liveCount; + + int value; + void *checkSum; +}; + +int Complex::liveCount = 0; + +Q_DECLARE_METATYPE(Complex); + +// Tests depend on the fact that: +Q_STATIC_ASSERT(!QTypeInfo<int>::isStatic); +Q_STATIC_ASSERT(!QTypeInfo<int>::isComplex); +Q_STATIC_ASSERT(!QTypeInfo<Movable>::isStatic); +Q_STATIC_ASSERT(QTypeInfo<Movable>::isComplex); +Q_STATIC_ASSERT(QTypeInfo<Complex>::isStatic); +Q_STATIC_ASSERT(QTypeInfo<Complex>::isComplex); + +class tst_QLinkedList : public QObject +{ + Q_OBJECT +private slots: + void eraseValidIteratorsOnSharedList() const; + void insertWithIteratorsOnSharedList() const; + void lengthInt() const; + void lengthMovable() const; + void lengthComplex() const; + void lengthSignature() const; + void firstInt() const; + void firstMovable() const; + void firstComplex() const; + void lastInt() const; + void lastMovable() const; + void lastComplex() const; + void beginInt() const; + void beginMovable() const; + void beginComplex() const; + void endInt() const; + void endMovable() const; + void endComplex() const; + void containsInt() const; + void containsMovable() const; + void containsComplex() const; + void countInt() const; + void countMovable() const; + void countComplex() const; + void cpp17ctad() const; + void emptyInt() const; + void emptyMovable() const; + void emptyComplex() const; + void endsWithInt() const; + void endsWithMovable() const; + void endsWithComplex() const; + void removeAllInt() const; + void removeAllMovable() const; + void removeAllComplex() const; + void removeOneInt() const; + void removeOneMovable() const; + void removeOneComplex() const; + void reverseIterators() const; + void startsWithInt() const; + void startsWithMovable() const; + void startsWithComplex() const; + void takeFirstInt() const; + void takeFirstMovable() const; + void takeFirstComplex() const; + void takeLastInt() const; + void takeLastMovable() const; + void takeLastComplex() const; + void toStdListInt() const; + void toStdListMovable() const; + void toStdListComplex() const; + void testOperatorsInt() const; + void testOperatorsMovable() const; + void testOperatorsComplex() const; + void testSTLIteratorsInt() const; + void testSTLIteratorsMovable() const; + void testSTLIteratorsComplex() const; + + void initializeList() const; + + void constSharedNullInt() const; + void constSharedNullMovable() const; + void constSharedNullComplex() const; + + void setSharableInt() const; +private: + template<typename T> void length() const; + template<typename T> void first() const; + template<typename T> void last() const; + template<typename T> void begin() const; + template<typename T> void end() const; + template<typename T> void contains() const; + template<typename T> void count() const; + template<typename T> void empty() const; + template<typename T> void endsWith() const; + template<typename T> void move() const; + template<typename T> void removeAll() const; + template<typename T> void removeOne() const; + template<typename T> void startsWith() const; + template<typename T> void swap() const; + template<typename T> void takeFirst() const; + template<typename T> void takeLast() const; + template<typename T> void toStdList() const; + template<typename T> void value() const; + + template<typename T> void testOperators() const; + template<typename T> void testSTLIterators() const; + + template<typename T> void constSharedNull() const; + + int dummyForGuard; +}; + +template<typename T> struct SimpleValue +{ + static T at(int index) + { + return values[index % maxSize]; + } + static const uint maxSize = 7; + static const T values[maxSize]; +}; + +template<> +const int SimpleValue<int>::values[] = { 10, 20, 30, 40, 100, 101, 102 }; +template<> +const Movable SimpleValue<Movable>::values[] = { 10, 20, 30, 40, 100, 101, 102 }; +template<> +const Complex SimpleValue<Complex>::values[] = { 10, 20, 30, 40, 100, 101, 102 }; + +// Make some macros for the tests to use in order to be slightly more readable... +#define T_FOO SimpleValue<T>::at(0) +#define T_BAR SimpleValue<T>::at(1) +#define T_BAZ SimpleValue<T>::at(2) +#define T_CAT SimpleValue<T>::at(3) +#define T_DOG SimpleValue<T>::at(4) +#define T_BLAH SimpleValue<T>::at(5) +#define T_WEEE SimpleValue<T>::at(6) + +template<typename T> +void tst_QLinkedList::length() const +{ + /* Empty list. */ + { + const QLinkedList<T> list; + QCOMPARE(list.size(), 0); + } + + /* One entry. */ + { + QLinkedList<T> list; + list.append(T_FOO); + QCOMPARE(list.size(), 1); + } + + /* Two entries. */ + { + QLinkedList<T> list; + list.append(T_FOO); + list.append(T_BAR); + QCOMPARE(list.size(), 2); + } + + /* Three entries. */ + { + QLinkedList<T> list; + list.append(T_FOO); + list.append(T_BAR); + list.append(T_BAZ); + QCOMPARE(list.size(), 3); + } +} + +void tst_QLinkedList::eraseValidIteratorsOnSharedList() const +{ + QLinkedList<int> a, b; + a.append(5); + a.append(10); + a.append(20); + a.append(20); + a.append(20); + a.append(20); + a.append(30); + + QLinkedList<int>::iterator i = a.begin(); + ++i; + ++i; + ++i; + b = a; + QLinkedList<int>::iterator r = a.erase(i); + QCOMPARE(b.size(), 7); + QCOMPARE(a.size(), 6); + --r; + --r; + QCOMPARE(*r, 10); // Ensure that number 2 instance was removed; +} + +void tst_QLinkedList::insertWithIteratorsOnSharedList() const +{ + QLinkedList<int> a, b; + a.append(5); + a.append(10); + a.append(20); + QLinkedList<int>::iterator i = a.begin(); + ++i; + ++i; + b = a; + + QLinkedList<int>::iterator i2 = a.insert(i, 15); + QCOMPARE(b.size(), 3); + QCOMPARE(a.size(), 4); + --i2; + QCOMPARE(*i2, 10); +} + +void tst_QLinkedList::lengthInt() const +{ + length<int>(); +} + +void tst_QLinkedList::lengthMovable() const +{ + const int liveCount = Movable::getLiveCount(); + length<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::lengthComplex() const +{ + const int liveCount = Complex::getLiveCount(); + length<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +void tst_QLinkedList::lengthSignature() const +{ + /* Constness. */ + { + const QLinkedList<int> list; + /* The function should be const. */ + list.size(); + } +} + +template<typename T> +void tst_QLinkedList::first() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR; + + QCOMPARE(list.first(), T_FOO); + + // remove an item, make sure it still works + list.pop_front(); + QVERIFY(list.size() == 1); + QCOMPARE(list.first(), T_BAR); +} + +void tst_QLinkedList::firstInt() const +{ + first<int>(); +} + +void tst_QLinkedList::firstMovable() const +{ + const int liveCount = Movable::getLiveCount(); + first<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::firstComplex() const +{ + const int liveCount = Complex::getLiveCount(); + first<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::last() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR; + + QCOMPARE(list.last(), T_BAR); + + // remove an item, make sure it still works + list.pop_back(); + QVERIFY(list.size() == 1); + QCOMPARE(list.last(), T_FOO); +} + +void tst_QLinkedList::lastInt() const +{ + last<int>(); +} + +void tst_QLinkedList::lastMovable() const +{ + const int liveCount = Movable::getLiveCount(); + last<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::lastComplex() const +{ + const int liveCount = Complex::getLiveCount(); + last<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::begin() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR; + + QCOMPARE(*list.begin(), T_FOO); + + // remove an item, make sure it still works + list.pop_front(); + QVERIFY(list.size() == 1); + QCOMPARE(*list.begin(), T_BAR); +} + +void tst_QLinkedList::beginInt() const +{ + begin<int>(); +} + +void tst_QLinkedList::beginMovable() const +{ + const int liveCount = Movable::getLiveCount(); + begin<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::beginComplex() const +{ + const int liveCount = Complex::getLiveCount(); + begin<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::end() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR; + + QCOMPARE(*--list.end(), T_BAR); + + // remove an item, make sure it still works + list.pop_back(); + QVERIFY(list.size() == 1); + QCOMPARE(*--list.end(), T_FOO); +} + +void tst_QLinkedList::endInt() const +{ + end<int>(); +} + +void tst_QLinkedList::endMovable() const +{ + const int liveCount = Movable::getLiveCount(); + end<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::endComplex() const +{ + const int liveCount = Complex::getLiveCount(); + end<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::contains() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + QVERIFY(list.contains(T_FOO)); + QVERIFY(list.contains(T_BLAH) != true); + + // add it and make sure it matches + list.append(T_BLAH); + QVERIFY(list.contains(T_BLAH)); +} + +void tst_QLinkedList::containsInt() const +{ + contains<int>(); +} + +void tst_QLinkedList::containsMovable() const +{ + const int liveCount = Movable::getLiveCount(); + contains<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::containsComplex() const +{ + const int liveCount = Complex::getLiveCount(); + contains<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::count() const +{ + QLinkedList<T> list; + + // starts empty + QVERIFY(list.count() == 0); + + // goes up + list.append(T_FOO); + QVERIFY(list.count() == 1); + + // and up + list.append(T_BAR); + QVERIFY(list.count() == 2); + + // and down + list.pop_back(); + QVERIFY(list.count() == 1); + + // and empty. :) + list.pop_back(); + QVERIFY(list.count() == 0); +} + +void tst_QLinkedList::countInt() const +{ + count<int>(); +} + +void tst_QLinkedList::countMovable() const +{ + const int liveCount = Movable::getLiveCount(); + count<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::countComplex() const +{ + const int liveCount = Complex::getLiveCount(); + count<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +void tst_QLinkedList::cpp17ctad() const +{ +#ifdef __cpp_deduction_guides +#define QVERIFY_IS_LIST_OF(obj, Type) \ + QVERIFY2((std::is_same<decltype(obj), QLinkedList<Type>>::value), \ + QMetaType::typeName(qMetaTypeId<decltype(obj)::value_type>())) +#define CHECK(Type, One, Two, Three) \ + do { \ + const Type v[] = {One, Two, Three}; \ + QLinkedList v1 = {One, Two, Three}; \ + QVERIFY_IS_LIST_OF(v1, Type); \ + QLinkedList v2(v1.begin(), v1.end()); \ + QVERIFY_IS_LIST_OF(v2, Type); \ + QLinkedList v3(std::begin(v), std::end(v)); \ + QVERIFY_IS_LIST_OF(v3, Type); \ + } while (false) \ + /*end*/ + CHECK(int, 1, 2, 3); + CHECK(double, 1.0, 2.0, 3.0); + CHECK(QString, QStringLiteral("one"), QStringLiteral("two"), QStringLiteral("three")); +#undef QVERIFY_IS_LIST_OF +#undef CHECK +#else + QSKIP("This test requires C++17 Constructor Template Argument Deduction support enabled in the compiler."); +#endif +} + +template<typename T> +void tst_QLinkedList::empty() const +{ + QLinkedList<T> list; + + // make sure it starts empty + QVERIFY(list.empty()); + + // and doesn't stay empty + list.append(T_FOO); + QVERIFY(!list.empty()); + + // and goes back to being empty + list.pop_back(); + QVERIFY(list.empty()); +} + +void tst_QLinkedList::emptyInt() const +{ + empty<int>(); +} + +void tst_QLinkedList::emptyMovable() const +{ + const int liveCount = Movable::getLiveCount(); + empty<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::emptyComplex() const +{ + const int liveCount = Complex::getLiveCount(); + empty<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::endsWith() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // test it returns correctly in both cases + QVERIFY(list.endsWith(T_BAZ)); + QVERIFY(!list.endsWith(T_BAR)); + + // remove an item and make sure the end item changes + list.pop_back(); + QVERIFY(list.endsWith(T_BAR)); +} + +void tst_QLinkedList::endsWithInt() const +{ + endsWith<int>(); +} + +void tst_QLinkedList::endsWithMovable() const +{ + const int liveCount = Movable::getLiveCount(); + endsWith<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::endsWithComplex() const +{ + const int liveCount = Complex::getLiveCount(); + endsWith<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::removeAll() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // remove one instance + list.removeAll(T_BAR); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ); + + // many instances + list << T_FOO << T_BAR << T_BAZ << T_FOO << T_BAR << T_BAZ << T_FOO << T_BAR << T_BAZ; + list.removeAll(T_BAR); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ); + + // try remove something that doesn't exist + list.removeAll(T_WEEE); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ << T_FOO << T_BAZ); +} + +void tst_QLinkedList::removeAllInt() const +{ + removeAll<int>(); +} + +void tst_QLinkedList::removeAllMovable() const +{ + const int liveCount = Movable::getLiveCount(); + removeAll<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::removeAllComplex() const +{ + const int liveCount = Complex::getLiveCount(); + removeAll<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::removeOne() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // middle + list.removeOne(T_BAR); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAZ); + + // start + list.removeOne(T_FOO); + QCOMPARE(list, QLinkedList<T>() << T_BAZ); + + // last + list.removeOne(T_BAZ); + QCOMPARE(list, QLinkedList<T>()); + + // make sure it really only removes one :) + list << T_FOO << T_FOO; + list.removeOne(T_FOO); + QCOMPARE(list, QLinkedList<T>() << T_FOO); + + // try remove something that doesn't exist + list.removeOne(T_WEEE); + QCOMPARE(list, QLinkedList<T>() << T_FOO); +} + +void tst_QLinkedList::removeOneInt() const +{ + removeOne<int>(); +} + +void tst_QLinkedList::removeOneMovable() const +{ + const int liveCount = Movable::getLiveCount(); + removeOne<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::removeOneComplex() const +{ + const int liveCount = Complex::getLiveCount(); + removeOne<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +void tst_QLinkedList::reverseIterators() const +{ + QLinkedList<int> l; + l << 1 << 2 << 3 << 4; + QLinkedList<int> lr = l; + std::reverse(lr.begin(), lr.end()); + const QLinkedList<int> &clr = lr; + QVERIFY(std::equal(l.begin(), l.end(), lr.rbegin())); + QVERIFY(std::equal(l.begin(), l.end(), lr.crbegin())); + QVERIFY(std::equal(l.begin(), l.end(), clr.rbegin())); + QVERIFY(std::equal(lr.rbegin(), lr.rend(), l.begin())); + QVERIFY(std::equal(lr.crbegin(), lr.crend(), l.begin())); + QVERIFY(std::equal(clr.rbegin(), clr.rend(), l.begin())); +} + +template<typename T> +void tst_QLinkedList::startsWith() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // make sure it starts ok + QVERIFY(list.startsWith(T_FOO)); + + // remove an item + list.removeFirst(); + QVERIFY(list.startsWith(T_BAR)); +} + +void tst_QLinkedList::startsWithInt() const +{ + startsWith<int>(); +} + +void tst_QLinkedList::startsWithMovable() const +{ + const int liveCount = Movable::getLiveCount(); + startsWith<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::startsWithComplex() const +{ + const int liveCount = Complex::getLiveCount(); + startsWith<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::takeFirst() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + QCOMPARE(list.takeFirst(), T_FOO); + QVERIFY(list.size() == 2); + QCOMPARE(list.takeFirst(), T_BAR); + QVERIFY(list.size() == 1); + QCOMPARE(list.takeFirst(), T_BAZ); + QVERIFY(list.size() == 0); +} + +void tst_QLinkedList::takeFirstInt() const +{ + takeFirst<int>(); +} + +void tst_QLinkedList::takeFirstMovable() const +{ + const int liveCount = Movable::getLiveCount(); + takeFirst<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::takeFirstComplex() const +{ + const int liveCount = Complex::getLiveCount(); + takeFirst<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::takeLast() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + QCOMPARE(list.takeLast(), T_BAZ); + QCOMPARE(list.takeLast(), T_BAR); + QCOMPARE(list.takeLast(), T_FOO); +} + +void tst_QLinkedList::takeLastInt() const +{ + takeLast<int>(); +} + +void tst_QLinkedList::takeLastMovable() const +{ + const int liveCount = Movable::getLiveCount(); + takeLast<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::takeLastComplex() const +{ + const int liveCount = Complex::getLiveCount(); + takeLast<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::toStdList() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + // yuck. + std::list<T> slist; + slist.push_back(T_FOO); + slist.push_back(T_BAR); + slist.push_back(T_BAZ); + + QCOMPARE(list.toStdList(), slist); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAR << T_BAZ); +} + +void tst_QLinkedList::toStdListInt() const +{ + toStdList<int>(); +} + +void tst_QLinkedList::toStdListMovable() const +{ + const int liveCount = Movable::getLiveCount(); + toStdList<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::toStdListComplex() const +{ + const int liveCount = Complex::getLiveCount(); + toStdList<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::testOperators() const +{ + QLinkedList<T> list; + list << T_FOO << T_BAR << T_BAZ; + + QLinkedList<T> listtwo; + listtwo << T_FOO << T_BAR << T_BAZ; + + // test equal + QVERIFY(list == listtwo); + + // not equal + listtwo.append(T_CAT); + QVERIFY(list != listtwo); + + // += + list += listtwo; + QVERIFY(list.size() == 7); + QVERIFY(listtwo.size() == 4); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAR << T_BAZ + << T_FOO << T_BAR << T_BAZ << T_CAT); + + // = + list = listtwo; + QCOMPARE(list, listtwo); + QCOMPARE(list, QLinkedList<T>() << T_FOO << T_BAR << T_BAZ << T_CAT); +} + +void tst_QLinkedList::testOperatorsInt() const +{ + testOperators<int>(); +} + +void tst_QLinkedList::testOperatorsMovable() const +{ + const int liveCount = Movable::getLiveCount(); + testOperators<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::testOperatorsComplex() const +{ + const int liveCount = Complex::getLiveCount(); + testOperators<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +template<typename T> +void tst_QLinkedList::testSTLIterators() const +{ + QLinkedList<T> list; + + // create a list + list << T_FOO << T_BAR << T_BAZ; + typename QLinkedList<T>::iterator it = list.begin(); + QCOMPARE(*it, T_FOO); it++; + QCOMPARE(*it, T_BAR); it++; + QCOMPARE(*it, T_BAZ); it++; + QCOMPARE(it, list.end()); it--; + + // walk backwards + QCOMPARE(*it, T_BAZ); it--; + QCOMPARE(*it, T_BAR); it--; + QCOMPARE(*it, T_FOO); + + // test erase + it = list.erase(it); + QVERIFY(list.size() == 2); + QCOMPARE(*it, T_BAR); + + // test multiple erase + it = list.erase(it, it + 2); + QVERIFY(list.size() == 0); + QCOMPARE(it, list.end()); + + // insert again + it = list.insert(it, T_FOO); + QVERIFY(list.size() == 1); + QCOMPARE(*it, T_FOO); + + // insert again + it = list.insert(it, T_BAR); + QVERIFY(list.size() == 2); + QCOMPARE(*it++, T_BAR); + QCOMPARE(*it, T_FOO); +} + +void tst_QLinkedList::testSTLIteratorsInt() const +{ + testSTLIterators<int>(); +} + +void tst_QLinkedList::testSTLIteratorsMovable() const +{ + const int liveCount = Movable::getLiveCount(); + testSTLIterators<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::testSTLIteratorsComplex() const +{ + const int liveCount = Complex::getLiveCount(); + testSTLIterators<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + +void tst_QLinkedList::initializeList() const +{ + QLinkedList<int> v1 { 2, 3, 4 }; + QCOMPARE(v1, QLinkedList<int>() << 2 << 3 << 4); + QCOMPARE(v1, (QLinkedList<int> { 2, 3, 4})); + + QLinkedList<QLinkedList<int>> v2{ v1, { 1 }, QLinkedList<int>(), { 2, 3, 4 } }; + QLinkedList<QLinkedList<int>> v3; + v3 << v1 << (QLinkedList<int>() << 1) << QLinkedList<int>() << v1; + QCOMPARE(v3, v2); +} + + +template<typename T> +void tst_QLinkedList::constSharedNull() const +{ + QLinkedList<T> list2; +#if !defined(QT_NO_UNSHARABLE_CONTAINERS) + QLinkedList<T> list1; + list1.setSharable(false); + QVERIFY(list1.isDetached()); + + list2.setSharable(true); +#endif + QVERIFY(!list2.isDetached()); +} + +void tst_QLinkedList::constSharedNullInt() const +{ + constSharedNull<int>(); +} + +void tst_QLinkedList::constSharedNullMovable() const +{ + const int liveCount = Movable::getLiveCount(); + constSharedNull<Movable>(); + QCOMPARE(liveCount, Movable::getLiveCount()); +} + +void tst_QLinkedList::constSharedNullComplex() const +{ + const int liveCount = Complex::getLiveCount(); + constSharedNull<Complex>(); + QCOMPARE(liveCount, Complex::getLiveCount()); +} + + +void tst_QLinkedList::setSharableInt() const +{ +#if !defined(QT_NO_UNSHARABLE_CONTAINERS) + QLinkedList<int> orglist; + orglist << 0 << 1 << 2 << 3 << 4 << 5; + int size = 6; + + QLinkedList<int> list; + list = orglist; + + QVERIFY(!list.isDetached()); + list.setSharable(true); + + QCOMPARE(list.size(), size); + + { + QLinkedList<int> copy(list); + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + list.setSharable(false); + QVERIFY(list.isDetached() || list.isSharedWith(QLinkedList<int>())); + + { + QLinkedList<int> copy(list); + + QVERIFY(copy.isDetached() || copy.isSharedWith(QLinkedList<int>())); + QCOMPARE(copy.size(), size); + QCOMPARE(copy, list); + } + + list.setSharable(true); + + { + QLinkedList<int> copy(list); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + QLinkedList<int>::const_iterator it = list.constBegin(); + for (int i = 0; i < list.size(); ++i) { + QCOMPARE(int(*it), i); + ++it; + } + + QCOMPARE(list.size(), size); +#endif +} + +QT_WARNING_POP +#else +class tst_QLinkedList : public QObject +{ + Q_OBJECT +private slots: + void initTestCase() { QSKIP("Deprecated APIs are disabled, skipping this test."); } +}; + +#endif // QT_DEPRECATED_SINCE(5, 15) + +QTEST_APPLESS_MAIN(tst_QLinkedList) +#include "tst_qlinkedlist.moc" diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp index 7418f447e6..ce88c6a7a4 100644 --- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp @@ -46,6 +46,7 @@ #include <private/qdrawhelper_p.h> #include <qpainter.h> +#include <qpainterpath.h> #include <qqueue.h> #include <qscreen.h> diff --git a/tests/auto/gui/painting/qregion/tst_qregion.cpp b/tests/auto/gui/painting/qregion/tst_qregion.cpp index d1ea7706b9..aa9a84ba8e 100644 --- a/tests/auto/gui/painting/qregion/tst_qregion.cpp +++ b/tests/auto/gui/painting/qregion/tst_qregion.cpp @@ -32,6 +32,7 @@ #include <qbitmap.h> #include <qpainter.h> +#include <qpainterpath.h> #include <qpolygon.h> class tst_QRegion : public QObject diff --git a/tests/auto/gui/painting/qtransform/tst_qtransform.cpp b/tests/auto/gui/painting/qtransform/tst_qtransform.cpp index 97c6ad60b8..2dcb564e2b 100644 --- a/tests/auto/gui/painting/qtransform/tst_qtransform.cpp +++ b/tests/auto/gui/painting/qtransform/tst_qtransform.cpp @@ -28,11 +28,11 @@ #include <QtTest/QtTest> -#include "qtransform.h" +#include <qpainterpath.h> #include <qpolygon.h> +#include <qtransform.h> #include <qdebug.h> - class tst_QTransform : public QObject { Q_OBJECT diff --git a/tests/auto/gui/qopengl/tst_qopengl.cpp b/tests/auto/gui/qopengl/tst_qopengl.cpp index 8dd8b95daf..ad0dfb5c08 100644 --- a/tests/auto/gui/qopengl/tst_qopengl.cpp +++ b/tests/auto/gui/qopengl/tst_qopengl.cpp @@ -36,6 +36,7 @@ #include <QtGui/QOpenGLFunctions> #include <QtGui/QOpenGLFunctions_4_2_Core> #include <QtGui/QPainter> +#include <QtGui/QPainterPath> #include <QtGui/QScreen> #include <QtGui/QWindow> #include <QtGui/QOffscreenSurface> diff --git a/tests/auto/gui/rhi/qrhi/data/buildshaders.bat b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat index 5b8a77b833..0cfeaaaff3 100644 --- a/tests/auto/gui/rhi/qrhi/data/buildshaders.bat +++ b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat @@ -44,5 +44,6 @@ qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simple.vert.qsb simple.vert qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simple.frag.qsb simple.frag qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.vert.qsb simpletextured.vert qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.frag.qsb simpletextured.frag +qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 20 -o simpletextured_array.frag.qsb simpletextured_array.frag qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.vert.qsb textured.vert qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.frag.qsb textured.frag diff --git a/tests/auto/gui/rhi/qrhi/data/simple.frag.qsb b/tests/auto/gui/rhi/qrhi/data/simple.frag.qsb Binary files differindex c235108d39..43edbdffd9 100644 --- a/tests/auto/gui/rhi/qrhi/data/simple.frag.qsb +++ b/tests/auto/gui/rhi/qrhi/data/simple.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simple.vert.qsb b/tests/auto/gui/rhi/qrhi/data/simple.vert.qsb Binary files differindex 68cfeb8f1d..06af5df492 100644 --- a/tests/auto/gui/rhi/qrhi/data/simple.vert.qsb +++ b/tests/auto/gui/rhi/qrhi/data/simple.vert.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simpletextured.frag.qsb b/tests/auto/gui/rhi/qrhi/data/simpletextured.frag.qsb Binary files differindex 397961c238..7749f3caad 100644 --- a/tests/auto/gui/rhi/qrhi/data/simpletextured.frag.qsb +++ b/tests/auto/gui/rhi/qrhi/data/simpletextured.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simpletextured.vert.qsb b/tests/auto/gui/rhi/qrhi/data/simpletextured.vert.qsb Binary files differindex a9067949a5..c87d4b2fc1 100644 --- a/tests/auto/gui/rhi/qrhi/data/simpletextured.vert.qsb +++ b/tests/auto/gui/rhi/qrhi/data/simpletextured.vert.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag b/tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag new file mode 100644 index 0000000000..c5ee2057d8 --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag @@ -0,0 +1,17 @@ +#version 440 + +layout(location = 0) in vec2 uv; +layout(location = 0) out vec4 fragColor; + +layout(binding = 0) uniform sampler2D tex[3]; + +void main() +{ + vec4 c0 = texture(tex[0], uv); + vec4 c1 = texture(tex[1], uv); + vec4 c2 = texture(tex[2], uv); + vec4 cc = c0 + c1 + c2; + vec4 c = vec4(clamp(cc.r, 0.0, 1.0), clamp(cc.g, 0.0, 1.0), clamp(cc.b, 0.0, 1.0), clamp(cc.a, 0.0, 1.0)); + c.rgb *= c.a; + fragColor = c; +} diff --git a/tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag.qsb b/tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag.qsb Binary files differnew file mode 100644 index 0000000000..362e220d25 --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletextured_array.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/textured.frag.qsb b/tests/auto/gui/rhi/qrhi/data/textured.frag.qsb Binary files differindex 018d732e2f..f669152c9c 100644 --- a/tests/auto/gui/rhi/qrhi/data/textured.frag.qsb +++ b/tests/auto/gui/rhi/qrhi/data/textured.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/textured.vert.qsb b/tests/auto/gui/rhi/qrhi/data/textured.vert.qsb Binary files differindex 44454d226e..d4ba474777 100644 --- a/tests/auto/gui/rhi/qrhi/data/textured.vert.qsb +++ b/tests/auto/gui/rhi/qrhi/data/textured.vert.qsb diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index 549481aa21..c1793d7d4a 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -91,6 +91,8 @@ private slots: void renderToTextureSimple(); void renderToTextureTexturedQuad_data(); void renderToTextureTexturedQuad(); + void renderToTextureArrayOfTexturedQuad_data(); + void renderToTextureArrayOfTexturedQuad(); void renderToTextureTexturedQuadAndUniformBuffer_data(); void renderToTextureTexturedQuadAndUniformBuffer(); void renderToWindowSimple_data(); @@ -1468,6 +1470,147 @@ void tst_QRhi::renderToTextureTexturedQuad() QVERIFY(qGreen(result.pixel(214, 191)) > 2 * qBlue(result.pixel(214, 191))); } +void tst_QRhi::renderToTextureArrayOfTexturedQuad_data() +{ + rhiTestData(); +} + +void tst_QRhi::renderToTextureArrayOfTexturedQuad() +{ + QFETCH(QRhi::Implementation, impl); + QFETCH(QRhiInitParams *, initParams); + + QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr)); + if (!rhi) + QSKIP("QRhi could not be created, skipping testing rendering"); + + QImage inputImage; + inputImage.load(QLatin1String(":/data/qt256.png")); + QVERIFY(!inputImage.isNull()); + + QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size(), 1, + QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + QVERIFY(texture->build()); + + QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ texture.data() })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor()); + rt->setRenderPassDescriptor(rpDesc.data()); + QVERIFY(rt->build()); + + QRhiCommandBuffer *cb = nullptr; + QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess); + QVERIFY(cb); + + QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch(); + + static const float verticesUvs[] = { + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f + }; + QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(verticesUvs))); + QVERIFY(vbuf->build()); + updates->uploadStaticBuffer(vbuf.data(), verticesUvs); + + // In this test we pass 3 textures (and samplers) to the fragment shader in + // form of an array of combined image samplers. + + QScopedPointer<QRhiTexture> inputTexture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size())); + QVERIFY(inputTexture->build()); + updates->uploadTexture(inputTexture.data(), inputImage); + + QImage redImage(inputImage.size(), QImage::Format_RGBA8888); + redImage.fill(Qt::red); + + QScopedPointer<QRhiTexture> redTexture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size())); + QVERIFY(redTexture->build()); + updates->uploadTexture(redTexture.data(), redImage); + + QImage greenImage(inputImage.size(), QImage::Format_RGBA8888); + greenImage.fill(Qt::green); + + QScopedPointer<QRhiTexture> greenTexture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size())); + QVERIFY(greenTexture->build()); + updates->uploadTexture(greenTexture.data(), greenImage); + + QScopedPointer<QRhiSampler> sampler(rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None, + QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge)); + QVERIFY(sampler->build()); + + QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings()); + QRhiShaderResourceBinding::TextureAndSampler texSamplers[3] = { + { inputTexture.data(), sampler.data() }, + { redTexture.data(), sampler.data() }, + { greenTexture.data(), sampler.data() } + }; + srb->setBindings({ + QRhiShaderResourceBinding::sampledTextures(0, QRhiShaderResourceBinding::FragmentStage, 3, texSamplers) + }); + QVERIFY(srb->build()); + + QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline()); + pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip); + QShader vs = loadShader(":/data/simpletextured.vert.qsb"); + QVERIFY(vs.isValid()); + QShader fs = loadShader(":/data/simpletextured_array.frag.qsb"); + QVERIFY(fs.isValid()); + pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ { 4 * sizeof(float) } }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) } + }); + pipeline->setVertexInputLayout(inputLayout); + pipeline->setShaderResourceBindings(srb.data()); + pipeline->setRenderPassDescriptor(rpDesc.data()); + + QVERIFY(pipeline->build()); + + cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 }, updates); + cb->setGraphicsPipeline(pipeline.data()); + cb->setShaderResources(); + cb->setViewport({ 0, 0, float(texture->pixelSize().width()), float(texture->pixelSize().height()) }); + QRhiCommandBuffer::VertexInput vbindings(vbuf.data(), 0); + cb->setVertexInput(0, 1, &vbindings); + cb->draw(4); + + QRhiReadbackResult readResult; + QImage result; + readResult.completed = [&readResult, &result] { + result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888_Premultiplied); + }; + QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch(); + readbackBatch->readBackTexture({ texture.data() }, &readResult); + cb->endPass(readbackBatch); + + rhi->endOffscreenFrame(); + + QVERIFY(!result.isNull()); + + if (impl == QRhi::Null) + return; + + // Flip with D3D and Metal because these have Y down in images. Vulkan does + // not need this because there Y is down both in images and in NDC, which + // just happens to give correct results with our OpenGL-targeted vertex and + // UV data. + if (rhi->isYUpInFramebuffer() != rhi->isYUpInNDC()) + result = std::move(result).mirrored(); + + // we added the input image + red + green together, so red and green must be all 1 + for (int y = 0; y < result.height(); ++y) { + for (int x = 0; x < result.width(); ++x) { + const QRgb pixel = result.pixel(x, y); + QCOMPARE(qRed(pixel), 255); + QCOMPARE(qGreen(pixel), 255); + } + } +} + void tst_QRhi::renderToTextureTexturedQuadAndUniformBuffer_data() { rhiTestData(); diff --git a/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp index a7a81e9f0b..6cd730bd38 100644 --- a/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp +++ b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp @@ -436,7 +436,7 @@ void tst_QSslCertificate::subjectAlternativeNames() void tst_QSslCertificate::utf8SubjectNames() { QSslCertificate cert = QSslCertificate::fromPath(testDataDir + "certificates/cert-ss-san-utf8.pem", QSsl::Pem, - QSslCertificate::FixedString).first(); + QSslCertificate::PatternSyntax::FixedString).first(); QVERIFY(!cert.isNull()); // O is "Heavy Metal Records" with heavy use of "decorations" like accents, umlauts etc., @@ -634,66 +634,66 @@ void tst_QSslCertificate::fromPath_qregularexpression_data() QTest::addColumn<bool>("pemencoding"); QTest::addColumn<int>("numCerts"); - QTest::newRow("empty fixed pem") << QString() << int(QSslCertificate::FixedString) << true << 0; - QTest::newRow("empty fixed der") << QString() << int(QSslCertificate::FixedString) << false << 0; - QTest::newRow("empty regexp pem") << QString() << int(QSslCertificate::RegExp) << true << 0; - QTest::newRow("empty regexp der") << QString() << int(QSslCertificate::RegExp) << false << 0; - QTest::newRow("empty wildcard pem") << QString() << int(QSslCertificate::Wildcard) << true << 0; - QTest::newRow("empty wildcard der") << QString() << int(QSslCertificate::Wildcard) << false << 0; - QTest::newRow("\"certificates\" fixed pem") << (testDataDir + "certificates") << int(QSslCertificate::FixedString) << true << 0; - QTest::newRow("\"certificates\" fixed der") << (testDataDir + "certificates") << int(QSslCertificate::FixedString) << false << 0; - QTest::newRow("\"certificates\" regexp pem") << (testDataDir + "certificates") << int(QSslCertificate::RegExp) << true << 0; - QTest::newRow("\"certificates\" regexp der") << (testDataDir + "certificates") << int(QSslCertificate::RegExp) << false << 0; - QTest::newRow("\"certificates\" wildcard pem") << (testDataDir + "certificates") << int(QSslCertificate::Wildcard) << true << 0; - QTest::newRow("\"certificates\" wildcard der") << (testDataDir + "certificates") << int(QSslCertificate::Wildcard) << false << 0; - QTest::newRow("\"certificates/cert.pem\" fixed pem") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::FixedString) << true << 1; - QTest::newRow("\"certificates/cert.pem\" fixed der") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::FixedString) << false << 0; - QTest::newRow("\"certificates/cert.pem\" regexp pem") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::RegExp) << true << 1; - QTest::newRow("\"certificates/cert.pem\" regexp der") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::RegExp) << false << 0; - QTest::newRow("\"certificates/cert.pem\" wildcard pem") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::Wildcard) << true << 1; - QTest::newRow("\"certificates/cert.pem\" wildcard der") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::Wildcard) << false << 0; - QTest::newRow("\"certificates/*\" fixed pem") << (testDataDir + "certificates/*") << int(QSslCertificate::FixedString) << true << 0; - QTest::newRow("\"certificates/*\" fixed der") << (testDataDir + "certificates/*") << int(QSslCertificate::FixedString) << false << 0; - QTest::newRow("\"certificates/*\" regexp pem") << (testDataDir + "certificates/*") << int(QSslCertificate::RegExp) << true << 0; - QTest::newRow("\"certificates/*\" regexp der") << (testDataDir + "certificates/*") << int(QSslCertificate::RegExp) << false << 0; - QTest::newRow("\"certificates/*\" wildcard pem") << (testDataDir + "certificates/*") << int(QSslCertificate::Wildcard) << true << 7; - QTest::newRow("\"certificates/ca*\" wildcard pem") << (testDataDir + "certificates/ca*") << int(QSslCertificate::Wildcard) << true << 1; - QTest::newRow("\"certificates/cert*\" wildcard pem") << (testDataDir + "certificates/cert*") << int(QSslCertificate::Wildcard) << true << 4; - QTest::newRow("\"certificates/cert-[sure]*\" wildcard pem") << (testDataDir + "certificates/cert-[sure]*") << int(QSslCertificate::Wildcard) << true << 3; - QTest::newRow("\"certificates/cert-[not]*\" wildcard pem") << (testDataDir + "certificates/cert-[not]*") << int(QSslCertificate::Wildcard) << true << 0; - QTest::newRow("\"certificates/*\" wildcard der") << (testDataDir + "certificates/*") << int(QSslCertificate::Wildcard) << false << 2; - QTest::newRow("\"c*/c*.pem\" fixed pem") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::FixedString) << true << 0; - QTest::newRow("\"c*/c*.pem\" fixed der") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::FixedString) << false << 0; - QTest::newRow("\"c*/c*.pem\" regexp pem") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::RegExp) << true << 0; - QTest::newRow("\"c*/c*.pem\" regexp der") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::RegExp) << false << 0; - QTest::newRow("\"c*/c*.pem\" wildcard pem") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::Wildcard) << true << 5; - QTest::newRow("\"c*/c*.pem\" wildcard der") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::Wildcard) << false << 0; - QTest::newRow("\"d*/c*.pem\" fixed pem") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::FixedString) << true << 0; - QTest::newRow("\"d*/c*.pem\" fixed der") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::FixedString) << false << 0; - QTest::newRow("\"d*/c*.pem\" regexp pem") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::RegExp) << true << 0; - QTest::newRow("\"d*/c*.pem\" regexp der") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::RegExp) << false << 0; - QTest::newRow("\"d*/c*.pem\" wildcard pem") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::Wildcard) << true << 0; - QTest::newRow("\"d*/c*.pem\" wildcard der") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::Wildcard) << false << 0; - QTest::newRow("\"c.*/c.*.pem\" fixed pem") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::FixedString) << true << 0; - QTest::newRow("\"c.*/c.*.pem\" fixed der") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::FixedString) << false << 0; - QTest::newRow("\"c.*/c.*.pem\" regexp pem") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::RegExp) << true << 5; - QTest::newRow("\"c.*/c.*.pem\" regexp der") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::RegExp) << false << 0; - QTest::newRow("\"c.*/c.*.pem\" wildcard pem") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::Wildcard) << true << 0; - QTest::newRow("\"c.*/c.*.pem\" wildcard der") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::Wildcard) << false << 0; - QTest::newRow("\"d.*/c.*.pem\" fixed pem") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::FixedString) << true << 0; - QTest::newRow("\"d.*/c.*.pem\" fixed der") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::FixedString) << false << 0; - QTest::newRow("\"d.*/c.*.pem\" regexp pem") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::RegExp) << true << 0; - QTest::newRow("\"d.*/c.*.pem\" regexp der") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::RegExp) << false << 0; - QTest::newRow("\"d.*/c.*.pem\" wildcard pem") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::Wildcard) << true << 0; - QTest::newRow("\"d.*/c.*.pem\" wildcard der") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::Wildcard) << false << 0; + QTest::newRow("empty fixed pem") << QString() << int(QSslCertificate::PatternSyntax::FixedString) << true << 0; + QTest::newRow("empty fixed der") << QString() << int(QSslCertificate::PatternSyntax::FixedString) << false << 0; + QTest::newRow("empty regexp pem") << QString() << int(QSslCertificate::PatternSyntax::RegularExpression) << true << 0; + QTest::newRow("empty regexp der") << QString() << int(QSslCertificate::PatternSyntax::RegularExpression) << false << 0; + QTest::newRow("empty wildcard pem") << QString() << int(QSslCertificate::PatternSyntax::Wildcard) << true << 0; + QTest::newRow("empty wildcard der") << QString() << int(QSslCertificate::PatternSyntax::Wildcard) << false << 0; + QTest::newRow("\"certificates\" fixed pem") << (testDataDir + "certificates") << int(QSslCertificate::PatternSyntax::FixedString) << true << 0; + QTest::newRow("\"certificates\" fixed der") << (testDataDir + "certificates") << int(QSslCertificate::PatternSyntax::FixedString) << false << 0; + QTest::newRow("\"certificates\" regexp pem") << (testDataDir + "certificates") << int(QSslCertificate::PatternSyntax::RegularExpression) << true << 0; + QTest::newRow("\"certificates\" regexp der") << (testDataDir + "certificates") << int(QSslCertificate::PatternSyntax::RegularExpression) << false << 0; + QTest::newRow("\"certificates\" wildcard pem") << (testDataDir + "certificates") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 0; + QTest::newRow("\"certificates\" wildcard der") << (testDataDir + "certificates") << int(QSslCertificate::PatternSyntax::Wildcard) << false << 0; + QTest::newRow("\"certificates/cert.pem\" fixed pem") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::PatternSyntax::FixedString) << true << 1; + QTest::newRow("\"certificates/cert.pem\" fixed der") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::PatternSyntax::FixedString) << false << 0; + QTest::newRow("\"certificates/cert.pem\" regexp pem") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << true << 1; + QTest::newRow("\"certificates/cert.pem\" regexp der") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << false << 0; + QTest::newRow("\"certificates/cert.pem\" wildcard pem") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 1; + QTest::newRow("\"certificates/cert.pem\" wildcard der") << (testDataDir + "certificates/cert.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << false << 0; + QTest::newRow("\"certificates/*\" fixed pem") << (testDataDir + "certificates/*") << int(QSslCertificate::PatternSyntax::FixedString) << true << 0; + QTest::newRow("\"certificates/*\" fixed der") << (testDataDir + "certificates/*") << int(QSslCertificate::PatternSyntax::FixedString) << false << 0; + QTest::newRow("\"certificates/*\" regexp pem") << (testDataDir + "certificates/*") << int(QSslCertificate::PatternSyntax::RegularExpression) << true << 0; + QTest::newRow("\"certificates/*\" regexp der") << (testDataDir + "certificates/*") << int(QSslCertificate::PatternSyntax::RegularExpression) << false << 0; + QTest::newRow("\"certificates/*\" wildcard pem") << (testDataDir + "certificates/*") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 7; + QTest::newRow("\"certificates/ca*\" wildcard pem") << (testDataDir + "certificates/ca*") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 1; + QTest::newRow("\"certificates/cert*\" wildcard pem") << (testDataDir + "certificates/cert*") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 4; + QTest::newRow("\"certificates/cert-[sure]*\" wildcard pem") << (testDataDir + "certificates/cert-[sure]*") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 3; + QTest::newRow("\"certificates/cert-[not]*\" wildcard pem") << (testDataDir + "certificates/cert-[not]*") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 0; + QTest::newRow("\"certificates/*\" wildcard der") << (testDataDir + "certificates/*") << int(QSslCertificate::PatternSyntax::Wildcard) << false << 2; + QTest::newRow("\"c*/c*.pem\" fixed pem") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::PatternSyntax::FixedString) << true << 0; + QTest::newRow("\"c*/c*.pem\" fixed der") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::PatternSyntax::FixedString) << false << 0; + QTest::newRow("\"c*/c*.pem\" regexp pem") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << true << 0; + QTest::newRow("\"c*/c*.pem\" regexp der") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << false << 0; + QTest::newRow("\"c*/c*.pem\" wildcard pem") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 5; + QTest::newRow("\"c*/c*.pem\" wildcard der") << (testDataDir + "c*/c*.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << false << 0; + QTest::newRow("\"d*/c*.pem\" fixed pem") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::PatternSyntax::FixedString) << true << 0; + QTest::newRow("\"d*/c*.pem\" fixed der") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::PatternSyntax::FixedString) << false << 0; + QTest::newRow("\"d*/c*.pem\" regexp pem") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << true << 0; + QTest::newRow("\"d*/c*.pem\" regexp der") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << false << 0; + QTest::newRow("\"d*/c*.pem\" wildcard pem") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 0; + QTest::newRow("\"d*/c*.pem\" wildcard der") << (testDataDir + "d*/c*.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << false << 0; + QTest::newRow("\"c.*/c.*.pem\" fixed pem") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::PatternSyntax::FixedString) << true << 0; + QTest::newRow("\"c.*/c.*.pem\" fixed der") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::PatternSyntax::FixedString) << false << 0; + QTest::newRow("\"c.*/c.*.pem\" regexp pem") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << true << 5; + QTest::newRow("\"c.*/c.*.pem\" regexp der") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << false << 0; + QTest::newRow("\"c.*/c.*.pem\" wildcard pem") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 0; + QTest::newRow("\"c.*/c.*.pem\" wildcard der") << (testDataDir + "c.*/c.*.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << false << 0; + QTest::newRow("\"d.*/c.*.pem\" fixed pem") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::PatternSyntax::FixedString) << true << 0; + QTest::newRow("\"d.*/c.*.pem\" fixed der") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::PatternSyntax::FixedString) << false << 0; + QTest::newRow("\"d.*/c.*.pem\" regexp pem") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << true << 0; + QTest::newRow("\"d.*/c.*.pem\" regexp der") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::PatternSyntax::RegularExpression) << false << 0; + QTest::newRow("\"d.*/c.*.pem\" wildcard pem") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 0; + QTest::newRow("\"d.*/c.*.pem\" wildcard der") << (testDataDir + "d.*/c.*.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << false << 0; #ifdef Q_OS_LINUX - QTest::newRow("absolute path wildcard pem") << (testDataDir + "certificates/*.pem") << int(QSslCertificate::Wildcard) << true << 7; + QTest::newRow("absolute path wildcard pem") << (testDataDir + "certificates/*.pem") << int(QSslCertificate::PatternSyntax::Wildcard) << true << 7; #endif - QTest::newRow("trailing-whitespace") << (testDataDir + "more-certificates/trailing-whitespace.pem") << int(QSslCertificate::FixedString) << true << 1; - QTest::newRow("no-ending-newline") << (testDataDir + "more-certificates/no-ending-newline.pem") << int(QSslCertificate::FixedString) << true << 1; - QTest::newRow("malformed-just-begin") << (testDataDir + "more-certificates/malformed-just-begin.pem") << int(QSslCertificate::FixedString) << true << 0; - QTest::newRow("malformed-just-begin-no-newline") << (testDataDir + "more-certificates/malformed-just-begin-no-newline.pem") << int(QSslCertificate::FixedString) << true << 0; + QTest::newRow("trailing-whitespace") << (testDataDir + "more-certificates/trailing-whitespace.pem") << int(QSslCertificate::PatternSyntax::FixedString) << true << 1; + QTest::newRow("no-ending-newline") << (testDataDir + "more-certificates/no-ending-newline.pem") << int(QSslCertificate::PatternSyntax::FixedString) << true << 1; + QTest::newRow("malformed-just-begin") << (testDataDir + "more-certificates/malformed-just-begin.pem") << int(QSslCertificate::PatternSyntax::FixedString) << true << 0; + QTest::newRow("malformed-just-begin-no-newline") << (testDataDir + "more-certificates/malformed-just-begin-no-newline.pem") << int(QSslCertificate::PatternSyntax::FixedString) << true << 0; } void tst_QSslCertificate::fromPath_qregularexpression() @@ -795,7 +795,7 @@ void tst_QSslCertificate::certInfo() "55:ba:e7:fb:95:5d:91"; QSslCertificate cert = QSslCertificate::fromPath(testDataDir + "certificates/cert.pem", QSsl::Pem, - QSslCertificate::FixedString).first(); + QSslCertificate::PatternSyntax::FixedString).first(); QVERIFY(!cert.isNull()); QCOMPARE(cert.issuerInfo(QSslCertificate::Organization)[0], QString("CryptSoft Pty Ltd")); @@ -852,7 +852,7 @@ void tst_QSslCertificate::certInfo() void tst_QSslCertificate::certInfoQByteArray() { QSslCertificate cert = QSslCertificate::fromPath(testDataDir + "certificates/cert.pem", QSsl::Pem, - QSslCertificate::FixedString).first(); + QSslCertificate::PatternSyntax::FixedString).first(); QVERIFY(!cert.isNull()); // in this test, check the bytearray variants before the enum variants to see if @@ -904,7 +904,7 @@ void tst_QSslCertificate::nulInCN() QSKIP("Generic QSslCertificatePrivate fails this test"); #endif QList<QSslCertificate> certList = - QSslCertificate::fromPath(testDataDir + "more-certificates/badguy-nul-cn.crt", QSsl::Pem, QSslCertificate::FixedString); + QSslCertificate::fromPath(testDataDir + "more-certificates/badguy-nul-cn.crt", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QCOMPARE(certList.size(), 1); const QSslCertificate &cert = certList.at(0); @@ -923,7 +923,7 @@ void tst_QSslCertificate::nulInSan() QSKIP("Generic QSslCertificatePrivate fails this test"); #endif QList<QSslCertificate> certList = - QSslCertificate::fromPath(testDataDir + "more-certificates/badguy-nul-san.crt", QSsl::Pem, QSslCertificate::FixedString); + QSslCertificate::fromPath(testDataDir + "more-certificates/badguy-nul-san.crt", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QCOMPARE(certList.size(), 1); const QSslCertificate &cert = certList.at(0); @@ -943,7 +943,7 @@ void tst_QSslCertificate::nulInSan() void tst_QSslCertificate::largeSerialNumber() { QList<QSslCertificate> certList = - QSslCertificate::fromPath(testDataDir + "more-certificates/cert-large-serial-number.pem", QSsl::Pem, QSslCertificate::FixedString); + QSslCertificate::fromPath(testDataDir + "more-certificates/cert-large-serial-number.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QCOMPARE(certList.size(), 1); @@ -955,7 +955,7 @@ void tst_QSslCertificate::largeSerialNumber() void tst_QSslCertificate::largeExpirationDate() // QTBUG-12489 { QList<QSslCertificate> certList = - QSslCertificate::fromPath(testDataDir + "more-certificates/cert-large-expiration-date.pem", QSsl::Pem, QSslCertificate::FixedString); + QSslCertificate::fromPath(testDataDir + "more-certificates/cert-large-expiration-date.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QCOMPARE(certList.size(), 1); @@ -968,7 +968,7 @@ void tst_QSslCertificate::largeExpirationDate() // QTBUG-12489 void tst_QSslCertificate::blacklistedCertificates() { - QList<QSslCertificate> blacklistedCerts = QSslCertificate::fromPath(testDataDir + "more-certificates/blacklisted*.pem", QSsl::Pem, QSslCertificate::Wildcard); + QList<QSslCertificate> blacklistedCerts = QSslCertificate::fromPath(testDataDir + "more-certificates/blacklisted*.pem", QSsl::Pem, QSslCertificate::PatternSyntax::Wildcard); QVERIFY(blacklistedCerts.count() > 0); for (int a = 0; a < blacklistedCerts.count(); a++) { QVERIFY(blacklistedCerts.at(a).isBlacklisted()); @@ -977,15 +977,15 @@ void tst_QSslCertificate::blacklistedCertificates() void tst_QSslCertificate::selfsignedCertificates() { - QVERIFY(QSslCertificate::fromPath(testDataDir + "certificates/cert-ss.pem", QSsl::Pem, QSslCertificate::FixedString).first().isSelfSigned()); - QVERIFY(!QSslCertificate::fromPath(testDataDir + "certificates/cert.pem", QSsl::Pem, QSslCertificate::FixedString).first().isSelfSigned()); + QVERIFY(QSslCertificate::fromPath(testDataDir + "certificates/cert-ss.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString).first().isSelfSigned()); + QVERIFY(!QSslCertificate::fromPath(testDataDir + "certificates/cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString).first().isSelfSigned()); QVERIFY(!QSslCertificate().isSelfSigned()); } void tst_QSslCertificate::toText() { QList<QSslCertificate> certList = - QSslCertificate::fromPath(testDataDir + "more-certificates/cert-large-expiration-date.pem", QSsl::Pem, QSslCertificate::FixedString); + QSslCertificate::fromPath(testDataDir + "more-certificates/cert-large-expiration-date.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QCOMPARE(certList.size(), 1); const QSslCertificate &cert = certList.at(0); @@ -1027,7 +1027,7 @@ void tst_QSslCertificate::toText() void tst_QSslCertificate::multipleCommonNames() { QList<QSslCertificate> certList = - QSslCertificate::fromPath(testDataDir + "more-certificates/test-cn-two-cns-cert.pem", QSsl::Pem, QSslCertificate::FixedString); + QSslCertificate::fromPath(testDataDir + "more-certificates/test-cn-two-cns-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QVERIFY(certList.count() > 0); QStringList commonNames = certList[0].subjectInfo(QSslCertificate::CommonName); @@ -1038,14 +1038,14 @@ void tst_QSslCertificate::multipleCommonNames() void tst_QSslCertificate::subjectAndIssuerAttributes() { QList<QSslCertificate> certList = - QSslCertificate::fromPath(testDataDir + "more-certificates/test-cn-with-drink-cert.pem", QSsl::Pem, QSslCertificate::FixedString); + QSslCertificate::fromPath(testDataDir + "more-certificates/test-cn-with-drink-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QVERIFY(certList.count() > 0); QList<QByteArray> attributes = certList[0].subjectInfoAttributes(); QVERIFY(attributes.contains(QByteArray("favouriteDrink"))); attributes.clear(); - certList = QSslCertificate::fromPath(testDataDir + "more-certificates/natwest-banking.pem", QSsl::Pem, QSslCertificate::FixedString); + certList = QSslCertificate::fromPath(testDataDir + "more-certificates/natwest-banking.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QVERIFY(certList.count() > 0); QByteArray shortName("1.3.6.1.4.1.311.60.2.1.3"); @@ -1080,17 +1080,17 @@ void tst_QSslCertificate::verify() errors.clear(); // Verify a valid cert signed by a CA - QList<QSslCertificate> caCerts = QSslCertificate::fromPath(testDataDir + "verify-certs/cacert.pem", QSsl::Pem, QSslCertificate::FixedString); + QList<QSslCertificate> caCerts = QSslCertificate::fromPath(testDataDir + "verify-certs/cacert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QSslSocket::addDefaultCaCertificate(caCerts.first()); - toVerify = QSslCertificate::fromPath(testDataDir + "verify-certs/test-ocsp-good-cert.pem", QSsl::Pem, QSslCertificate::FixedString); + toVerify = QSslCertificate::fromPath(testDataDir + "verify-certs/test-ocsp-good-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); errors = QSslCertificate::verify(toVerify); VERIFY_VERBOSE(errors.count() == 0); errors.clear(); // Test a blacklisted certificate - toVerify = QSslCertificate::fromPath(testDataDir + "verify-certs/test-addons-mozilla-org-cert.pem", QSsl::Pem, QSslCertificate::FixedString); + toVerify = QSslCertificate::fromPath(testDataDir + "verify-certs/test-addons-mozilla-org-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); errors = QSslCertificate::verify(toVerify); bool foundBlack = false; foreach (const QSslError &error, errors) { @@ -1103,7 +1103,7 @@ void tst_QSslCertificate::verify() errors.clear(); // This one is expired and untrusted - toVerify = QSslCertificate::fromPath(testDataDir + "more-certificates/cert-large-serial-number.pem", QSsl::Pem, QSslCertificate::FixedString); + toVerify = QSslCertificate::fromPath(testDataDir + "more-certificates/cert-large-serial-number.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); errors = QSslCertificate::verify(toVerify); VERIFY_VERBOSE(errors.contains(QSslError(QSslError::SelfSignedCertificate, toVerify[0]))); VERIFY_VERBOSE(errors.contains(QSslError(QSslError::CertificateExpired, toVerify[0]))); @@ -1111,15 +1111,15 @@ void tst_QSslCertificate::verify() toVerify.clear(); // This one is signed by a valid cert, but the signer is not a valid CA - toVerify << QSslCertificate::fromPath(testDataDir + "verify-certs/test-intermediate-not-ca-cert.pem", QSsl::Pem, QSslCertificate::FixedString).first(); - toVerify << QSslCertificate::fromPath(testDataDir + "verify-certs/test-ocsp-good-cert.pem", QSsl::Pem, QSslCertificate::FixedString).first(); + toVerify << QSslCertificate::fromPath(testDataDir + "verify-certs/test-intermediate-not-ca-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString).first(); + toVerify << QSslCertificate::fromPath(testDataDir + "verify-certs/test-ocsp-good-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString).first(); errors = QSslCertificate::verify(toVerify); VERIFY_VERBOSE(errors.contains(QSslError(QSslError::InvalidCaCertificate, toVerify[1]))); toVerify.clear(); // This one is signed by a valid cert, and the signer is a valid CA - toVerify << QSslCertificate::fromPath(testDataDir + "verify-certs/test-intermediate-is-ca-cert.pem", QSsl::Pem, QSslCertificate::FixedString).first(); - toVerify << QSslCertificate::fromPath(testDataDir + "verify-certs/test-intermediate-ca-cert.pem", QSsl::Pem, QSslCertificate::FixedString).first(); + toVerify << QSslCertificate::fromPath(testDataDir + "verify-certs/test-intermediate-is-ca-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString).first(); + toVerify << QSslCertificate::fromPath(testDataDir + "verify-certs/test-intermediate-ca-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString).first(); errors = QSslCertificate::verify(toVerify); VERIFY_VERBOSE(errors.count() == 0); @@ -1149,7 +1149,7 @@ QString tst_QSslCertificate::toString(const QList<QSslError>& errors) void tst_QSslCertificate::extensions() { QList<QSslCertificate> certList = - QSslCertificate::fromPath(testDataDir + "more-certificates/natwest-banking.pem", QSsl::Pem, QSslCertificate::FixedString); + QSslCertificate::fromPath(testDataDir + "more-certificates/natwest-banking.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QVERIFY(certList.count() > 0); QSslCertificate cert = certList[0]; @@ -1247,7 +1247,7 @@ void tst_QSslCertificate::extensions() void tst_QSslCertificate::extensionsCritical() { QList<QSslCertificate> certList = - QSslCertificate::fromPath(testDataDir + "verify-certs/test-addons-mozilla-org-cert.pem", QSsl::Pem, QSslCertificate::FixedString); + QSslCertificate::fromPath(testDataDir + "verify-certs/test-addons-mozilla-org-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QVERIFY(certList.count() > 0); QSslCertificate cert = certList[0]; @@ -1368,12 +1368,12 @@ void tst_QSslCertificate::version_data() QTest::newRow("null certificate") << QSslCertificate() << QByteArray(); QList<QSslCertificate> certs; - certs << QSslCertificate::fromPath(testDataDir + "verify-certs/test-ocsp-good-cert.pem", QSsl::Pem, QSslCertificate::FixedString); + certs << QSslCertificate::fromPath(testDataDir + "verify-certs/test-ocsp-good-cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QTest::newRow("v3 certificate") << certs.first() << QByteArrayLiteral("3"); certs.clear(); - certs << QSslCertificate::fromPath(testDataDir + "certificates/cert.pem", QSsl::Pem, QSslCertificate::FixedString); + certs << QSslCertificate::fromPath(testDataDir + "certificates/cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QTest::newRow("v1 certificate") << certs.first() << QByteArrayLiteral("1"); } @@ -1410,7 +1410,7 @@ void tst_QSslCertificate::pkcs12() QVERIFY(ok); f.close(); - QList<QSslCertificate> leafCert = QSslCertificate::fromPath(testDataDir + QLatin1String("pkcs12/leaf.crt"), QSsl::Pem, QSslCertificate::FixedString); + QList<QSslCertificate> leafCert = QSslCertificate::fromPath(testDataDir + QLatin1String("pkcs12/leaf.crt"), QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QVERIFY(!leafCert.isEmpty()); QCOMPARE(cert, leafCert.first()); @@ -1425,7 +1425,7 @@ void tst_QSslCertificate::pkcs12() QVERIFY(!leafKey.isNull()); QCOMPARE(key, leafKey); - QList<QSslCertificate> caCert = QSslCertificate::fromPath(testDataDir + QLatin1String("pkcs12/inter.crt"), QSsl::Pem, QSslCertificate::FixedString); + QList<QSslCertificate> caCert = QSslCertificate::fromPath(testDataDir + QLatin1String("pkcs12/inter.crt"), QSsl::Pem, QSslCertificate::PatternSyntax::FixedString); QVERIFY(!caCert.isEmpty()); QVERIFY(!caCerts.isEmpty()); diff --git a/tests/auto/opengl/qgl/tst_qgl.cpp b/tests/auto/opengl/qgl/tst_qgl.cpp new file mode 100644 index 0000000000..d860caaa37 --- /dev/null +++ b/tests/auto/opengl/qgl/tst_qgl.cpp @@ -0,0 +1,2525 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <qcoreapplication.h> +#include <qdebug.h> +#include <qgl.h> +#include <qglpixelbuffer.h> +#include <qglframebufferobject.h> +#include <qglcolormap.h> +#include <qpaintengine.h> +#include <qpainterpath.h> +#include <qopenglfunctions.h> +#include <qopenglframebufferobject.h> +#include <qopenglpaintdevice.h> + +#include <QGraphicsView> +#include <QGraphicsProxyWidget> +#include <QVBoxLayout> + +#ifdef QT_BUILD_INTERNAL +#include <qpa/qplatformpixmap.h> +#include <QtOpenGL/private/qgl_p.h> +#include <QtGui/private/qimage_p.h> +#include <QtGui/private/qimagepixmapcleanuphooks_p.h> +#include <QtGui/private/qopenglextensions_p.h> +#endif + +class tst_QGL : public QObject +{ +Q_OBJECT + +public: + tst_QGL(); + virtual ~tst_QGL(); + + static void initMain(); + +private slots: + void initTestCase(); + void getSetCheck(); +#ifdef QT_BUILD_INTERNAL + void qglContextDefaultBindTexture(); + void openGLVersionCheck(); + void shareRegister(); + void textureCleanup(); +#endif + void partialGLWidgetUpdates_data(); + void partialGLWidgetUpdates(); + void glWidgetWithAlpha(); + void glWidgetRendering(); + void glFBOSimpleRendering(); + void glFBORendering(); + void currentFboSync(); + void multipleFBOInterleavedRendering(); + void glFBOUseInGLWidget(); + void glPBufferRendering(); + void glWidgetReparent(); + void glWidgetRenderPixmap(); + void colormap(); + void fboFormat(); + void testDontCrashOnDanglingResources(); + void replaceClipping(); + void clipTest(); + void destroyFBOAfterContext(); + void threadImages(); + void nullRectCrash(); + void graphicsViewClipping(); + void extensions(); +}; + +tst_QGL::tst_QGL() +{ +} + +tst_QGL::~tst_QGL() +{ +} + +void tst_QGL::initMain() +{ + QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); +} + +void tst_QGL::initTestCase() +{ + QGLWidget glWidget; + if (!glWidget.isValid()) + QSKIP("QGL is not supported on the test system"); +} + +class MyGLContext : public QGLContext +{ +public: + MyGLContext(const QGLFormat& format) : QGLContext(format) {} + bool windowCreated() const { return QGLContext::windowCreated(); } + void setWindowCreated(bool on) { QGLContext::setWindowCreated(on); } + bool initialized() const { return QGLContext::initialized(); } + void setInitialized(bool on) { QGLContext::setInitialized(on); } +}; + +class MyGLWidget : public QGLWidget +{ +public: + MyGLWidget() : QGLWidget() {} + bool autoBufferSwap() const { return QGLWidget::autoBufferSwap(); } + void setAutoBufferSwap(bool on) { QGLWidget::setAutoBufferSwap(on); } +}; + +static int appDefaultDepth() +{ + static int depth = 0; + if (depth == 0) { + QPixmap pm(1, 1); + depth = pm.depth(); + } + return depth; +} + +// Using INT_MIN and INT_MAX will cause failures on systems +// where "int" is 64-bit, so use the explicit values instead. +#define TEST_INT_MIN (-2147483647 - 1) +#define TEST_INT_MAX 2147483647 + +// Testing get/set functions +void tst_QGL::getSetCheck() +{ + QGLFormat obj1; + // int QGLFormat::depthBufferSize() + // void QGLFormat::setDepthBufferSize(int) + QCOMPARE(-1, obj1.depthBufferSize()); + obj1.setDepthBufferSize(0); + QCOMPARE(0, obj1.depthBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -2147483648"); + obj1.setDepthBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.depthBufferSize()); // Makes no sense with a negative buffer size + obj1.setDepthBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -1"); + obj1.setDepthBufferSize(-1); + QCOMPARE(3, obj1.depthBufferSize()); + obj1.setDepthBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.depthBufferSize()); + + // int QGLFormat::accumBufferSize() + // void QGLFormat::setAccumBufferSize(int) + QCOMPARE(-1, obj1.accumBufferSize()); + obj1.setAccumBufferSize(0); + QCOMPARE(0, obj1.accumBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -2147483648"); + obj1.setAccumBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.accumBufferSize()); // Makes no sense with a negative buffer size + obj1.setAccumBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -1"); + obj1.setAccumBufferSize(-1); + QCOMPARE(3, obj1.accumBufferSize()); + obj1.setAccumBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.accumBufferSize()); + + // int QGLFormat::redBufferSize() + // void QGLFormat::setRedBufferSize(int) + QCOMPARE(-1, obj1.redBufferSize()); + obj1.setRedBufferSize(0); + QCOMPARE(0, obj1.redBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -2147483648"); + obj1.setRedBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.redBufferSize()); // Makes no sense with a negative buffer size + obj1.setRedBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -1"); + obj1.setRedBufferSize(-1); + QCOMPARE(3, obj1.redBufferSize()); + obj1.setRedBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.redBufferSize()); + + // int QGLFormat::greenBufferSize() + // void QGLFormat::setGreenBufferSize(int) + QCOMPARE(-1, obj1.greenBufferSize()); + obj1.setGreenBufferSize(0); + QCOMPARE(0, obj1.greenBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -2147483648"); + obj1.setGreenBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.greenBufferSize()); // Makes no sense with a negative buffer size + obj1.setGreenBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -1"); + obj1.setGreenBufferSize(-1); + QCOMPARE(3, obj1.greenBufferSize()); + obj1.setGreenBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.greenBufferSize()); + + // int QGLFormat::blueBufferSize() + // void QGLFormat::setBlueBufferSize(int) + QCOMPARE(-1, obj1.blueBufferSize()); + obj1.setBlueBufferSize(0); + QCOMPARE(0, obj1.blueBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -2147483648"); + obj1.setBlueBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.blueBufferSize()); // Makes no sense with a negative buffer size + obj1.setBlueBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -1"); + obj1.setBlueBufferSize(-1); + QCOMPARE(3, obj1.blueBufferSize()); + obj1.setBlueBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.blueBufferSize()); + + // int QGLFormat::alphaBufferSize() + // void QGLFormat::setAlphaBufferSize(int) + QCOMPARE(-1, obj1.alphaBufferSize()); + QCOMPARE(false, obj1.alpha()); + QVERIFY(!obj1.testOption(QGL::AlphaChannel)); + QVERIFY(obj1.testOption(QGL::NoAlphaChannel)); + obj1.setAlphaBufferSize(1); + QCOMPARE(true, obj1.alpha()); // setAlphaBufferSize() enables alpha. + QCOMPARE(1, obj1.alphaBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -2147483648"); + obj1.setAlphaBufferSize(TEST_INT_MIN); + QCOMPARE(1, obj1.alphaBufferSize()); // Makes no sense with a negative buffer size + obj1.setAlphaBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -1"); + obj1.setAlphaBufferSize(-1); + QCOMPARE(3, obj1.alphaBufferSize()); + obj1.setAlphaBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.alphaBufferSize()); + + // int QGLFormat::stencilBufferSize() + // void QGLFormat::setStencilBufferSize(int) + QCOMPARE(-1, obj1.stencilBufferSize()); + obj1.setStencilBufferSize(1); + QCOMPARE(1, obj1.stencilBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -2147483648"); + obj1.setStencilBufferSize(TEST_INT_MIN); + QCOMPARE(1, obj1.stencilBufferSize()); // Makes no sense with a negative buffer size + obj1.setStencilBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -1"); + obj1.setStencilBufferSize(-1); + QCOMPARE(3, obj1.stencilBufferSize()); + obj1.setStencilBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.stencilBufferSize()); + + // bool QGLFormat::sampleBuffers() + // void QGLFormat::setSampleBuffers(bool) + QCOMPARE(false, obj1.sampleBuffers()); + QVERIFY(!obj1.testOption(QGL::SampleBuffers)); + QVERIFY(obj1.testOption(QGL::NoSampleBuffers)); + + obj1.setSampleBuffers(false); + QCOMPARE(false, obj1.sampleBuffers()); + QVERIFY(obj1.testOption(QGL::NoSampleBuffers)); + obj1.setSampleBuffers(true); + QCOMPARE(true, obj1.sampleBuffers()); + QVERIFY(obj1.testOption(QGL::SampleBuffers)); + + // int QGLFormat::samples() + // void QGLFormat::setSamples(int) + QCOMPARE(-1, obj1.samples()); + obj1.setSamples(0); + QCOMPARE(0, obj1.samples()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -2147483648"); + obj1.setSamples(TEST_INT_MIN); + QCOMPARE(0, obj1.samples()); // Makes no sense with a negative sample size + obj1.setSamples(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -1"); + obj1.setSamples(-1); + QCOMPARE(3, obj1.samples()); + obj1.setSamples(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.samples()); + + // int QGLFormat::swapInterval() + // void QGLFormat::setSwapInterval(int) + QCOMPARE(-1, obj1.swapInterval()); + obj1.setSwapInterval(0); + QCOMPARE(0, obj1.swapInterval()); + obj1.setSwapInterval(TEST_INT_MIN); + QCOMPARE(TEST_INT_MIN, obj1.swapInterval()); + obj1.setSwapInterval(-1); + QCOMPARE(-1, obj1.swapInterval()); + obj1.setSwapInterval(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.swapInterval()); + + // bool QGLFormat::doubleBuffer() + // void QGLFormat::setDoubleBuffer(bool) + QCOMPARE(true, obj1.doubleBuffer()); + QVERIFY(obj1.testOption(QGL::DoubleBuffer)); + QVERIFY(!obj1.testOption(QGL::SingleBuffer)); + obj1.setDoubleBuffer(false); + QCOMPARE(false, obj1.doubleBuffer()); + QVERIFY(!obj1.testOption(QGL::DoubleBuffer)); + QVERIFY(obj1.testOption(QGL::SingleBuffer)); + obj1.setDoubleBuffer(true); + QCOMPARE(true, obj1.doubleBuffer()); + QVERIFY(obj1.testOption(QGL::DoubleBuffer)); + QVERIFY(!obj1.testOption(QGL::SingleBuffer)); + + // bool QGLFormat::depth() + // void QGLFormat::setDepth(bool) + QCOMPARE(true, obj1.depth()); + QVERIFY(obj1.testOption(QGL::DepthBuffer)); + QVERIFY(!obj1.testOption(QGL::NoDepthBuffer)); + obj1.setDepth(false); + QCOMPARE(false, obj1.depth()); + QVERIFY(!obj1.testOption(QGL::DepthBuffer)); + QVERIFY(obj1.testOption(QGL::NoDepthBuffer)); + obj1.setDepth(true); + QCOMPARE(true, obj1.depth()); + QVERIFY(obj1.testOption(QGL::DepthBuffer)); + QVERIFY(!obj1.testOption(QGL::NoDepthBuffer)); + + // bool QGLFormat::rgba() + // void QGLFormat::setRgba(bool) + QCOMPARE(true, obj1.rgba()); + QVERIFY(obj1.testOption(QGL::Rgba)); + QVERIFY(!obj1.testOption(QGL::ColorIndex)); + obj1.setRgba(false); + QCOMPARE(false, obj1.rgba()); + QVERIFY(!obj1.testOption(QGL::Rgba)); + QVERIFY(obj1.testOption(QGL::ColorIndex)); + obj1.setRgba(true); + QCOMPARE(true, obj1.rgba()); + QVERIFY(obj1.testOption(QGL::Rgba)); + QVERIFY(!obj1.testOption(QGL::ColorIndex)); + + // bool QGLFormat::alpha() + // void QGLFormat::setAlpha(bool) + QVERIFY(obj1.testOption(QGL::AlphaChannel)); + QVERIFY(!obj1.testOption(QGL::NoAlphaChannel)); + obj1.setAlpha(false); + QCOMPARE(false, obj1.alpha()); + QVERIFY(!obj1.testOption(QGL::AlphaChannel)); + QVERIFY(obj1.testOption(QGL::NoAlphaChannel)); + obj1.setAlpha(true); + QCOMPARE(true, obj1.alpha()); + QVERIFY(obj1.testOption(QGL::AlphaChannel)); + QVERIFY(!obj1.testOption(QGL::NoAlphaChannel)); + + // bool QGLFormat::accum() + // void QGLFormat::setAccum(bool) + obj1.setAccumBufferSize(0); + QCOMPARE(false, obj1.accum()); + QVERIFY(!obj1.testOption(QGL::AccumBuffer)); + QVERIFY(obj1.testOption(QGL::NoAccumBuffer)); + obj1.setAccum(false); + QCOMPARE(false, obj1.accum()); + QVERIFY(!obj1.testOption(QGL::AccumBuffer)); + QVERIFY(obj1.testOption(QGL::NoAccumBuffer)); + obj1.setAccum(true); + QCOMPARE(true, obj1.accum()); + QVERIFY(obj1.testOption(QGL::AccumBuffer)); + QVERIFY(!obj1.testOption(QGL::NoAccumBuffer)); + + // bool QGLFormat::stencil() + // void QGLFormat::setStencil(bool) + QCOMPARE(true, obj1.stencil()); + QVERIFY(obj1.testOption(QGL::StencilBuffer)); + QVERIFY(!obj1.testOption(QGL::NoStencilBuffer)); + obj1.setStencil(false); + QCOMPARE(false, obj1.stencil()); + QVERIFY(!obj1.testOption(QGL::StencilBuffer)); + QVERIFY(obj1.testOption(QGL::NoStencilBuffer)); + obj1.setStencil(true); + QCOMPARE(true, obj1.stencil()); + QVERIFY(obj1.testOption(QGL::StencilBuffer)); + QVERIFY(!obj1.testOption(QGL::NoStencilBuffer)); + + // bool QGLFormat::stereo() + // void QGLFormat::setStereo(bool) + QCOMPARE(false, obj1.stereo()); + QVERIFY(!obj1.testOption(QGL::StereoBuffers)); + QVERIFY(obj1.testOption(QGL::NoStereoBuffers)); + obj1.setStereo(false); + QCOMPARE(false, obj1.stereo()); + QVERIFY(!obj1.testOption(QGL::StereoBuffers)); + QVERIFY(obj1.testOption(QGL::NoStereoBuffers)); + obj1.setStereo(true); + QCOMPARE(true, obj1.stereo()); + QVERIFY(obj1.testOption(QGL::StereoBuffers)); + QVERIFY(!obj1.testOption(QGL::NoStereoBuffers)); + + // bool QGLFormat::directRendering() + // void QGLFormat::setDirectRendering(bool) + QCOMPARE(true, obj1.directRendering()); + QVERIFY(obj1.testOption(QGL::DirectRendering)); + QVERIFY(!obj1.testOption(QGL::IndirectRendering)); + obj1.setDirectRendering(false); + QCOMPARE(false, obj1.directRendering()); + QVERIFY(!obj1.testOption(QGL::DirectRendering)); + QVERIFY(obj1.testOption(QGL::IndirectRendering)); + obj1.setDirectRendering(true); + QCOMPARE(true, obj1.directRendering()); + QVERIFY(obj1.testOption(QGL::DirectRendering)); + QVERIFY(!obj1.testOption(QGL::IndirectRendering)); + + // bool QGLFormat::overlay() + // void QGLFormat::setOverlay(bool) + QCOMPARE(false, obj1.hasOverlay()); + QVERIFY(!obj1.testOption(QGL::HasOverlay)); + QVERIFY(obj1.testOption(QGL::NoOverlay)); + obj1.setOverlay(false); + QCOMPARE(false, obj1.hasOverlay()); + QVERIFY(!obj1.testOption(QGL::HasOverlay)); + QVERIFY(obj1.testOption(QGL::NoOverlay)); + obj1.setOverlay(true); + QCOMPARE(true, obj1.hasOverlay()); + QVERIFY(obj1.testOption(QGL::HasOverlay)); + QVERIFY(!obj1.testOption(QGL::NoOverlay)); + + // int QGLFormat::plane() + // void QGLFormat::setPlane(int) + QCOMPARE(0, obj1.plane()); + obj1.setPlane(0); + QCOMPARE(0, obj1.plane()); + obj1.setPlane(TEST_INT_MIN); + QCOMPARE(TEST_INT_MIN, obj1.plane()); + obj1.setPlane(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.plane()); + + // int QGLFormat::major/minorVersion() + // void QGLFormat::setVersion(int, int) + QCOMPARE(obj1.majorVersion(), 2); + QCOMPARE(obj1.minorVersion(), 0); + obj1.setVersion(3, 2); + QCOMPARE(obj1.majorVersion(), 3); + QCOMPARE(obj1.minorVersion(), 2); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setVersion: Cannot set zero or negative version number 0.1"); + obj1.setVersion(0, 1); + QCOMPARE(obj1.majorVersion(), 3); + QCOMPARE(obj1.minorVersion(), 2); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setVersion: Cannot set zero or negative version number 3.-1"); + obj1.setVersion(3, -1); + QCOMPARE(obj1.majorVersion(), 3); + QCOMPARE(obj1.minorVersion(), 2); + obj1.setVersion(TEST_INT_MAX, TEST_INT_MAX - 1); + QCOMPARE(obj1.majorVersion(), TEST_INT_MAX); + QCOMPARE(obj1.minorVersion(), TEST_INT_MAX - 1); + + + // operator== and operator!= for QGLFormat + QGLFormat format1; + QGLFormat format2; + + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + format1.setDoubleBuffer(false); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setDoubleBuffer(false); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setDepthBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setDepthBufferSize(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setAccumBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setAccumBufferSize(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setRedBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setRedBufferSize(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setGreenBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setGreenBufferSize(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setBlueBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setBlueBufferSize(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setAlphaBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setAlphaBufferSize(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setStencilBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setStencilBufferSize(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setSamples(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setSamples(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setSwapInterval(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setSwapInterval(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setPlane(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setPlane(8); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setVersion(3, 2); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setVersion(3, 2); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setProfile(QGLFormat::CoreProfile); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setProfile(QGLFormat::CoreProfile); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + format1.setOption(QGL::NoDeprecatedFunctions); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setOption(QGL::NoDeprecatedFunctions); + QCOMPARE(format1, format2); + QVERIFY(!(format1 != format2)); + + // Copy constructor and assignment for QGLFormat. + QGLFormat format3(format1); + QGLFormat format4; + QCOMPARE(format1, format3); + QVERIFY(format1 != format4); + format4 = format1; + QCOMPARE(format1, format4); + + // Check that modifying a copy doesn't affect the original. + format3.setRedBufferSize(16); + format4.setPlane(16); + QCOMPARE(format1.redBufferSize(), 8); + QCOMPARE(format1.plane(), 8); + + // Check the QGLFormat constructor that takes an option list. + QGLFormat format5 + (QGL::DepthBuffer | QGL::StereoBuffers | QGL::ColorIndex, 3); + QVERIFY(format5.depth()); + QVERIFY(format5.stereo()); + QVERIFY(format5.doubleBuffer()); // From defaultFormat() + QVERIFY(!format5.hasOverlay()); // From defaultFormat() + QVERIFY(!format5.rgba()); + QCOMPARE(format5.plane(), 3); + + // The default format should be the same as QGLFormat(). + QCOMPARE(QGLFormat::defaultFormat(), QGLFormat()); + + // Modify the default format and check that it was changed. + QGLFormat::setDefaultFormat(format1); + QCOMPARE(QGLFormat::defaultFormat(), format1); + + // Restore the default format. + QGLFormat::setDefaultFormat(QGLFormat()); + QCOMPARE(QGLFormat::defaultFormat(), QGLFormat()); + + // Check the default overlay format's expected values. + QGLFormat overlay(QGLFormat::defaultOverlayFormat()); + QCOMPARE(overlay.depthBufferSize(), -1); + QCOMPARE(overlay.accumBufferSize(), -1); + QCOMPARE(overlay.redBufferSize(), -1); + QCOMPARE(overlay.greenBufferSize(), -1); + QCOMPARE(overlay.blueBufferSize(), -1); + QCOMPARE(overlay.alphaBufferSize(), -1); + QCOMPARE(overlay.samples(), -1); + QCOMPARE(overlay.swapInterval(), -1); + QCOMPARE(overlay.plane(), 1); + QVERIFY(!overlay.sampleBuffers()); + QVERIFY(!overlay.doubleBuffer()); + QVERIFY(!overlay.depth()); + QVERIFY(!overlay.rgba()); + QVERIFY(!overlay.alpha()); + QVERIFY(!overlay.accum()); + QVERIFY(!overlay.stencil()); + QVERIFY(!overlay.stereo()); + QVERIFY(overlay.directRendering()); // Only option that should be on. + QVERIFY(!overlay.hasOverlay()); // Overlay doesn't need an overlay! + + // Modify the default overlay format and check that it was changed. + QGLFormat::setDefaultOverlayFormat(format1); + QCOMPARE(QGLFormat::defaultOverlayFormat(), format1); + + // Restore the default overlay format. + QGLFormat::setDefaultOverlayFormat(overlay); + QCOMPARE(QGLFormat::defaultOverlayFormat(), overlay); + + MyGLContext obj2(obj1); + // bool QGLContext::windowCreated() + // void QGLContext::setWindowCreated(bool) + obj2.setWindowCreated(false); + QCOMPARE(false, obj2.windowCreated()); + obj2.setWindowCreated(true); + QCOMPARE(true, obj2.windowCreated()); + + // bool QGLContext::initialized() + // void QGLContext::setInitialized(bool) + obj2.setInitialized(false); + QCOMPARE(false, obj2.initialized()); + obj2.setInitialized(true); + QCOMPARE(true, obj2.initialized()); + + MyGLWidget obj3; + // bool QGLWidget::autoBufferSwap() + // void QGLWidget::setAutoBufferSwap(bool) + obj3.setAutoBufferSwap(false); + QCOMPARE(false, obj3.autoBufferSwap()); + obj3.setAutoBufferSwap(true); + QCOMPARE(true, obj3.autoBufferSwap()); +} + +#ifdef QT_BUILD_INTERNAL +QT_BEGIN_NAMESPACE +extern QGLFormat::OpenGLVersionFlags qOpenGLVersionFlagsFromString(const QString &versionString); +QT_END_NAMESPACE +#endif + +#ifdef QT_BUILD_INTERNAL +void tst_QGL::openGLVersionCheck() +{ + QString versionString; + QGLFormat::OpenGLVersionFlags expectedFlag; + QGLFormat::OpenGLVersionFlags versionFlag; + + versionString = "1.1 Irix 6.5"; + expectedFlag = QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.2 Microsoft"; + expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.2.1"; + expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.3 NVIDIA"; + expectedFlag = QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.4"; + expectedFlag = QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.5 NVIDIA"; + expectedFlag = QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "2.0.2 NVIDIA 87.62"; + expectedFlag = QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "2.1 NVIDIA"; + expectedFlag = QGLFormat::OpenGL_Version_2_1 | QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "2.1"; + expectedFlag = QGLFormat::OpenGL_Version_2_1 | QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES-CM 1.0 ATI"; + expectedFlag = QGLFormat::OpenGL_ES_Common_Version_1_0 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES-CL 1.0 ATI"; + expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_0; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES-CM 1.1 ATI"; + expectedFlag = QGLFormat::OpenGL_ES_Common_Version_1_1 | QGLFormat::OpenGL_ES_CommonLite_Version_1_1 | QGLFormat::OpenGL_ES_Common_Version_1_0 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES-CL 1.1 ATI"; + expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_1 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES 2.0 ATI"; + expectedFlag = QGLFormat::OpenGL_ES_Version_2_0; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "3.0"; + expectedFlag = QGLFormat::OpenGL_Version_3_0 | QGLFormat::OpenGL_Version_2_1 | QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + QGLWidget glWidget; + glWidget.show(); + glWidget.makeCurrent(); + + // This is unfortunately the only test we can make on the actual openGLVersionFlags() + // However, the complicated parts are in openGLVersionFlags(const QString &versionString) + // tested above + +#if defined(QT_OPENGL_ES_2) + QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0); +#else + if (QOpenGLContext::currentContext()->isOpenGLES()) + QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0); + else + QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_1); +#endif //defined(QT_OPENGL_ES_2) +} +#endif //QT_BUILD_INTERNAL + +static bool fuzzyComparePixels(const QRgb testPixel, const QRgb refPixel, const char* file, int line, int x = -1, int y = -1) +{ + static int maxFuzz = 1; + static bool maxFuzzSet = false; + + // On 16 bpp systems, we need to allow for more fuzz: + if (!maxFuzzSet) { + maxFuzzSet = true; + if (appDefaultDepth() < 24) + maxFuzz = 32; + } + + int redFuzz = qAbs(qRed(testPixel) - qRed(refPixel)); + int greenFuzz = qAbs(qGreen(testPixel) - qGreen(refPixel)); + int blueFuzz = qAbs(qBlue(testPixel) - qBlue(refPixel)); + int alphaFuzz = qAbs(qAlpha(testPixel) - qAlpha(refPixel)); + + if (refPixel != 0 && testPixel == 0) { + QString msg; + if (x >= 0) { + msg = QString("Test pixel [%1, %2] is null (black) when it should be (%3,%4,%5,%6)") + .arg(x).arg(y) + .arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel)); + } else { + msg = QString("Test pixel is null (black) when it should be (%2,%3,%4,%5)") + .arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel)); + } + + QTest::qFail(msg.toLatin1(), file, line); + return false; + } + + if (redFuzz > maxFuzz || greenFuzz > maxFuzz || blueFuzz > maxFuzz || alphaFuzz > maxFuzz) { + QString msg; + + if (x >= 0) + msg = QString("Pixel [%1,%2]: ").arg(x).arg(y); + else + msg = QString("Pixel "); + + msg += QString("Max fuzz (%1) exceeded: (%2,%3,%4,%5) vs (%6,%7,%8,%9)") + .arg(maxFuzz) + .arg(qRed(testPixel)).arg(qGreen(testPixel)).arg(qBlue(testPixel)).arg(qAlpha(testPixel)) + .arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel)); + QTest::qFail(msg.toLatin1(), file, line); + return false; + } + return true; +} + +static void fuzzyCompareImages(const QImage &testImage, const QImage &referenceImage, const char* file, int line) +{ + QCOMPARE(testImage.width(), referenceImage.width()); + QCOMPARE(testImage.height(), referenceImage.height()); + + for (int y = 0; y < testImage.height(); y++) { + for (int x = 0; x < testImage.width(); x++) { + if (!fuzzyComparePixels(testImage.pixel(x, y), referenceImage.pixel(x, y), file, line, x, y)) { + // Might as well save the images for easier debugging: + referenceImage.save("referenceImage.png"); + testImage.save("testImage.png"); + return; + } + } + } +} + +#define QFUZZY_COMPARE_IMAGES(A,B) \ + fuzzyCompareImages(A, B, __FILE__, __LINE__) + +#define QFUZZY_COMPARE_PIXELS(A,B) \ + fuzzyComparePixels(A, B, __FILE__, __LINE__) + +class UnclippedWidget : public QWidget +{ +public: + bool painted; + + UnclippedWidget() + : painted(false) + { + } + + void paintEvent(QPaintEvent *) + { + QPainter p(this); + p.fillRect(rect().adjusted(-1000, -1000, 1000, 1000), Qt::black); + + painted = true; + } +}; + +void tst_QGL::graphicsViewClipping() +{ + const int size = 64; + UnclippedWidget *widget = new UnclippedWidget; + widget->setFixedSize(size, size); + + QGraphicsScene scene; + + scene.addWidget(widget)->setPos(0, 0); + + QGraphicsView view(&scene); + // Use Qt::Tool as fully decorated windows have a minimum width of 160 on Windows. + view.setWindowFlags(view.windowFlags() | Qt::Tool); + view.setBackgroundBrush(Qt::white); + view.resize(2*size, 2*size); + + QGLWidget *viewport = new QGLWidget; + view.setViewport(viewport); + view.show(); + qApp->setActiveWindow(&view); + + if (!viewport->isValid()) + return; + + scene.setSceneRect(view.viewport()->rect()); + + QVERIFY(QTest::qWaitForWindowExposed(view.viewport()->windowHandle())); + #ifdef Q_OS_MAC + // The black rectangle jumps from the center to the upper left for some reason. + QTest::qWait(100); + #endif + + QTRY_VERIFY(widget->painted); + + QImage image = viewport->grabFrameBuffer(); + QImage expected = image; + + QPainter p(&expected); + p.fillRect(expected.rect(), Qt::white); + p.fillRect(QRect(0, 0, size, size), Qt::black); + p.end(); + + QFUZZY_COMPARE_IMAGES(image, expected); +} + +void tst_QGL::partialGLWidgetUpdates_data() +{ + QTest::addColumn<bool>("doubleBufferedContext"); + QTest::addColumn<bool>("autoFillBackground"); + QTest::addColumn<bool>("supportsPartialUpdates"); + + QTest::newRow("Double buffered context") << true << true << false; + QTest::newRow("Double buffered context without auto-fill background") << true << false << false; + QTest::newRow("Single buffered context") << false << true << false; + QTest::newRow("Single buffered context without auto-fill background") << false << false << true; +} + +void tst_QGL::partialGLWidgetUpdates() +{ + QFETCH(bool, doubleBufferedContext); + QFETCH(bool, autoFillBackground); + QFETCH(bool, supportsPartialUpdates); + + class MyGLWidget : public QGLWidget + { + public: + QRegion paintEventRegion; + void paintEvent(QPaintEvent *e) + { + paintEventRegion = e->region(); + } + }; + + QGLFormat format = QGLFormat::defaultFormat(); + format.setDoubleBuffer(doubleBufferedContext); + QGLFormat::setDefaultFormat(format); + + MyGLWidget widget; + widget.setFixedSize(150, 150); + widget.setAutoFillBackground(autoFillBackground); + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + QCoreApplication::processEvents(); // Process all queued paint events + + if (widget.format().doubleBuffer() != doubleBufferedContext) + QSKIP("Platform does not support requested format"); + + widget.paintEventRegion = QRegion(); + widget.repaint(50, 50, 50, 50); + + if (supportsPartialUpdates) + QCOMPARE(widget.paintEventRegion, QRegion(50, 50, 50, 50)); + else + QCOMPARE(widget.paintEventRegion, QRegion(widget.rect())); +} + + +// This tests that rendering to a QGLPBuffer using QPainter works. +void tst_QGL::glPBufferRendering() +{ + if (!QGLPixelBuffer::hasOpenGLPbuffers()) + QSKIP("QGLPixelBuffer not supported on this platform"); + + QGLPixelBuffer* pbuf = new QGLPixelBuffer(128, 128); + + QPainter p; + bool begun = p.begin(pbuf); + QVERIFY(begun); + + QPaintEngine::Type engineType = p.paintEngine()->type(); + QVERIFY(engineType == QPaintEngine::OpenGL || engineType == QPaintEngine::OpenGL2); + + p.fillRect(0, 0, 128, 128, Qt::red); + p.fillRect(32, 32, 64, 64, Qt::blue); + p.end(); + + QImage fb = pbuf->toImage(); + delete pbuf; + + QImage reference(128, 128, fb.format()); + p.begin(&reference); + p.fillRect(0, 0, 128, 128, Qt::red); + p.fillRect(32, 32, 64, 64, Qt::blue); + p.end(); + + QFUZZY_COMPARE_IMAGES(fb, reference); +} + +void tst_QGL::glWidgetWithAlpha() +{ + QGLWidget* w = new QGLWidget(QGLFormat(QGL::AlphaChannel)); + w->show(); + QVERIFY(QTest::qWaitForWindowExposed(w)); + + delete w; +} + + +void qt_opengl_draw_test_pattern(QPainter* painter, int width, int height) +{ + QPainterPath intersectingPath; + intersectingPath.moveTo(0, 0); + intersectingPath.lineTo(100, 0); + intersectingPath.lineTo(0, 100); + intersectingPath.lineTo(100, 100); + intersectingPath.closeSubpath(); + + QPainterPath trianglePath; + trianglePath.moveTo(50, 0); + trianglePath.lineTo(100, 100); + trianglePath.lineTo(0, 100); + trianglePath.closeSubpath(); + + painter->setTransform(QTransform()); // reset xform + painter->fillRect(-1, -1, width+2, height+2, Qt::red); // Background + painter->translate(14, 14); + painter->fillPath(intersectingPath, Qt::blue); // Test stencil buffer works + painter->translate(128, 0); + painter->setClipPath(trianglePath); // Test depth buffer works + painter->setTransform(QTransform()); // reset xform ready for fill + painter->fillRect(-1, -1, width+2, height+2, Qt::green); +} + +void qt_opengl_check_test_pattern(const QImage& img) +{ + // As we're doing more than trivial painting, we can't just compare to + // an image rendered with raster. Instead, we sample at well-defined + // test-points: + QFUZZY_COMPARE_PIXELS(img.pixel(39, 64), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(img.pixel(89, 64), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(img.pixel(64, 39), QColor(Qt::blue).rgb()); + QFUZZY_COMPARE_PIXELS(img.pixel(64, 89), QColor(Qt::blue).rgb()); + + QFUZZY_COMPARE_PIXELS(img.pixel(167, 39), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(img.pixel(217, 39), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(img.pixel(192, 64), QColor(Qt::green).rgb()); +} + +class GLWidget : public QGLWidget +{ +public: + GLWidget(QWidget* p = 0) + : QGLWidget(p), beginOk(false), engineType(QPaintEngine::MaxUser) {} + bool beginOk; + QPaintEngine::Type engineType; + void paintGL() + { + QPainter p; + beginOk = p.begin(this); + QPaintEngine* pe = p.paintEngine(); + engineType = pe->type(); + + qt_opengl_draw_test_pattern(&p, width(), height()); + + // No p.end() or swap buffers, should be done automatically + } + +}; + +void tst_QGL::glWidgetRendering() +{ + GLWidget w; + w.resize(256, 128); + w.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QVERIFY(w.beginOk); + QVERIFY(w.engineType == QPaintEngine::OpenGL || w.engineType == QPaintEngine::OpenGL2); + +#if defined(Q_OS_QNX) + // glReadPixels reads from the back buffer. On QNX the buffer is not preserved + // after a buffer swap. This is why we have to swap the buffer explicitly before calling + // grabFrameBuffer to retrieve the content of the front buffer. + w.swapBuffers(); +#endif + QImage fb = w.grabFrameBuffer(false); + qt_opengl_check_test_pattern(fb); +} + +void tst_QGL::glFBOSimpleRendering() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform"); + + QGLWidget glw; + glw.makeCurrent(); + + // No multisample with combined depth/stencil attachment: + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::NoAttachment); + + QGLFramebufferObject *fbo = new QGLFramebufferObject(200, 100, fboFormat); + + fbo->bind(); + + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + funcs->glClearColor(1.0, 0.0, 0.0, 1.0); + funcs->glClear(GL_COLOR_BUFFER_BIT); + funcs->glFinish(); + + QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32); + QImage reference(fb.size(), QImage::Format_RGB32); + reference.fill(0xffff0000); + + QFUZZY_COMPARE_IMAGES(fb, reference); + + delete fbo; +} + +// NOTE: This tests that CombinedDepthStencil attachment works by assuming the +// GL2 engine is being used and is implemented the same way as it was when +// this autotest was written. If this is not the case, there may be some +// false-positives: I.e. The test passes when either the depth or stencil +// buffer is actually missing. But that's probably ok anyway. +void tst_QGL::glFBORendering() +{ +#if defined(Q_OS_QNX) + QSKIP("Reading the QGLFramebufferObject is unsupported on this platform"); +#endif + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform"); + + QGLWidget glw; + glw.makeCurrent(); + + // No multisample with combined depth/stencil attachment: + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + + // Don't complicate things by using NPOT: + QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat); + + if (fbo->attachment() != QGLFramebufferObject::CombinedDepthStencil) { + delete fbo; + QSKIP("FBOs missing combined depth~stencil support"); + } + + QPainter fboPainter; + bool painterBegun = fboPainter.begin(fbo); + QVERIFY(painterBegun); + + qt_opengl_draw_test_pattern(&fboPainter, fbo->width(), fbo->height()); + + fboPainter.end(); + + QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32); + delete fbo; + + qt_opengl_check_test_pattern(fb); +} + +class QOpenGLFramebufferObjectPaintDevice : public QOpenGLPaintDevice +{ +public: + QOpenGLFramebufferObjectPaintDevice(int width, int height) + : QOpenGLPaintDevice(width, height) + , m_fbo(width, height, QOpenGLFramebufferObject::CombinedDepthStencil) + { + } + + void ensureActiveTarget() + { + m_fbo.bind(); + } + + QImage toImage() const + { + return m_fbo.toImage(); + } + +private: + QOpenGLFramebufferObject m_fbo; +}; + +void tst_QGL::currentFboSync() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform"); + +#if defined(Q_OS_QNX) + QSKIP("Reading the QGLFramebufferObject is unsupported on this platform"); +#endif + + QGLWidget glw; + glw.makeCurrent(); + + // For some reason we offer inter-operatibility between QGL and QOpenGL + // paint engines. (?!) Let's check if the two engines can be used to perform + // drawing in turns on different targets within the same context. + + { + QGLFramebufferObject fbo1(256, 256, QGLFramebufferObject::CombinedDepthStencil); + + QOpenGLFramebufferObjectPaintDevice fbo2(256, 256); + + QImage sourceImage(256, 256, QImage::Format_ARGB32_Premultiplied); + QPainter sourcePainter(&sourceImage); + qt_opengl_draw_test_pattern(&sourcePainter, 256, 256); + + QPainter fbo1Painter(&fbo1); + + QPainter fbo2Painter(&fbo2); + fbo2Painter.drawImage(0, 0, sourceImage); + fbo2Painter.end(); + + QImage fbo2Image = fbo2.toImage(); + + fbo1Painter.drawImage(0, 0, sourceImage); + fbo1Painter.end(); + + QGLFramebufferObject::bindDefault(); + + // Convert the QGLFBO's result since QOpenGLFBO uses a wider + // variety of possible return formats. + QCOMPARE(fbo1.toImage().convertToFormat(fbo2Image.format()), fbo2Image); + } + + { + QGLFramebufferObject fbo1(512, 512, QGLFramebufferObject::CombinedDepthStencil); + + QOpenGLFramebufferObjectPaintDevice fbo2(256, 256); + + QImage sourceImage(256, 256, QImage::Format_ARGB32_Premultiplied); + QPainter sourcePainter(&sourceImage); + qt_opengl_draw_test_pattern(&sourcePainter, 256, 256); + + QPainter fbo2Painter(&fbo2); + fbo2Painter.drawImage(0, 0, sourceImage); + QImage fbo2Image1 = fbo2.toImage(); + fbo2Painter.fillRect(0, 0, 256, 256, Qt::white); + + QPainter fbo1Painter(&fbo1); + fbo1Painter.drawImage(0, 0, sourceImage); + fbo1Painter.end(); + + // check that the OpenGL paint engine now knows it needs to sync + fbo2Painter.drawImage(0, 0, sourceImage); + QImage fbo2Image2 = fbo2.toImage(); + + fbo2Painter.end(); + + QCOMPARE(fbo2Image1, fbo2Image2); + } +} + +// Tests multiple QPainters active on different FBOs at the same time, with +// interleaving painting. Performance-wise, this is sub-optimal, but it still +// has to work flawlessly +void tst_QGL::multipleFBOInterleavedRendering() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform"); + + QGLWidget glw; + glw.makeCurrent(); + + // No multisample with combined depth/stencil attachment: + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + + QGLFramebufferObject *fbo1 = new QGLFramebufferObject(256, 128, fboFormat); + QGLFramebufferObject *fbo2 = new QGLFramebufferObject(256, 128, fboFormat); + QGLFramebufferObject *fbo3 = new QGLFramebufferObject(256, 128, fboFormat); + + if ( (fbo1->attachment() != QGLFramebufferObject::CombinedDepthStencil) || + (fbo2->attachment() != QGLFramebufferObject::CombinedDepthStencil) || + (fbo3->attachment() != QGLFramebufferObject::CombinedDepthStencil) ) + { + delete fbo1; + delete fbo2; + delete fbo3; + QSKIP("FBOs missing combined depth~stencil support"); + } + + QPainter fbo1Painter; + QPainter fbo2Painter; + QPainter fbo3Painter; + + QVERIFY(fbo1Painter.begin(fbo1)); + QVERIFY(fbo2Painter.begin(fbo2)); + QVERIFY(fbo3Painter.begin(fbo3)); + + // Confirm we're using the GL2 engine, as interleaved rendering isn't supported + // on the GL1 engine: + if (fbo1Painter.paintEngine()->type() != QPaintEngine::OpenGL2) + QSKIP("Interleaved GL rendering requires OpenGL 2.0 or higher"); + + QPainterPath intersectingPath; + intersectingPath.moveTo(0, 0); + intersectingPath.lineTo(100, 0); + intersectingPath.lineTo(0, 100); + intersectingPath.lineTo(100, 100); + intersectingPath.closeSubpath(); + + QPainterPath trianglePath; + trianglePath.moveTo(50, 0); + trianglePath.lineTo(100, 100); + trianglePath.lineTo(0, 100); + trianglePath.closeSubpath(); + + fbo1Painter.fillRect(0, 0, fbo1->width(), fbo1->height(), Qt::red); // Background + fbo2Painter.fillRect(0, 0, fbo2->width(), fbo2->height(), Qt::green); // Background + fbo3Painter.fillRect(0, 0, fbo3->width(), fbo3->height(), Qt::blue); // Background + + fbo1Painter.translate(14, 14); + fbo2Painter.translate(14, 14); + fbo3Painter.translate(14, 14); + + fbo1Painter.fillPath(intersectingPath, Qt::blue); // Test stencil buffer works + fbo2Painter.fillPath(intersectingPath, Qt::red); // Test stencil buffer works + fbo3Painter.fillPath(intersectingPath, Qt::green); // Test stencil buffer works + + fbo1Painter.translate(128, 0); + fbo2Painter.translate(128, 0); + fbo3Painter.translate(128, 0); + + fbo1Painter.setClipPath(trianglePath); + fbo2Painter.setClipPath(trianglePath); + fbo3Painter.setClipPath(trianglePath); + + fbo1Painter.setTransform(QTransform()); // reset xform + fbo2Painter.setTransform(QTransform()); // reset xform + fbo3Painter.setTransform(QTransform()); // reset xform + + fbo1Painter.fillRect(0, 0, fbo1->width(), fbo1->height(), Qt::green); + fbo2Painter.fillRect(0, 0, fbo2->width(), fbo2->height(), Qt::blue); + fbo3Painter.fillRect(0, 0, fbo3->width(), fbo3->height(), Qt::red); + + fbo1Painter.end(); + fbo2Painter.end(); + fbo3Painter.end(); + + QImage fb1 = fbo1->toImage().convertToFormat(QImage::Format_RGB32); + QImage fb2 = fbo2->toImage().convertToFormat(QImage::Format_RGB32); + QImage fb3 = fbo3->toImage().convertToFormat(QImage::Format_RGB32); + delete fbo1; + delete fbo2; + delete fbo3; + + // As we're doing more than trivial painting, we can't just compare to + // an image rendered with raster. Instead, we sample at well-defined + // test-points: + QFUZZY_COMPARE_PIXELS(fb1.pixel(39, 64), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(fb1.pixel(89, 64), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 39), QColor(Qt::blue).rgb()); + QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 89), QColor(Qt::blue).rgb()); + QFUZZY_COMPARE_PIXELS(fb1.pixel(167, 39), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(fb1.pixel(217, 39), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(fb1.pixel(192, 64), QColor(Qt::green).rgb()); + + QFUZZY_COMPARE_PIXELS(fb2.pixel(39, 64), QColor(Qt::green).rgb()); + QFUZZY_COMPARE_PIXELS(fb2.pixel(89, 64), QColor(Qt::green).rgb()); + QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 39), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 89), QColor(Qt::red).rgb()); + QFUZZY_COMPARE_PIXELS(fb2.pixel(167, 39), QColor(Qt::green).rgb()); + QFUZZY_COMPARE_PIXELS(fb2.pixel(217, 39), QColor(Qt::green).rgb()); + QFUZZY_COMPARE_PIXELS(fb2.pixel(192, 64), QColor(Qt::blue).rgb()); + + QFUZZY_COMPARE_PIXELS(fb3.pixel(39, 64), QColor(Qt::blue).rgb()); + QFUZZY_COMPARE_PIXELS(fb3.pixel(89, 64), QColor(Qt::blue).rgb()); + QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 39), QColor(Qt::green).rgb()); + QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 89), QColor(Qt::green).rgb()); + QFUZZY_COMPARE_PIXELS(fb3.pixel(167, 39), QColor(Qt::blue).rgb()); + QFUZZY_COMPARE_PIXELS(fb3.pixel(217, 39), QColor(Qt::blue).rgb()); + QFUZZY_COMPARE_PIXELS(fb3.pixel(192, 64), QColor(Qt::red).rgb()); +} + +class FBOUseInGLWidget : public QGLWidget +{ +public: + bool widgetPainterBeginOk; + bool fboPainterBeginOk; + QImage fboImage; +protected: + void paintEvent(QPaintEvent*) + { + QPainter widgetPainter; + widgetPainterBeginOk = widgetPainter.begin(this); + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::NoAttachment); + QGLFramebufferObject *fbo = new QGLFramebufferObject(100, 100, fboFormat); + + QPainter fboPainter; + fboPainterBeginOk = fboPainter.begin(fbo); + fboPainter.fillRect(-1, -1, 130, 130, Qt::red); + fboPainter.end(); + fboImage = fbo->toImage(); + + widgetPainter.fillRect(-1, -1, width()+2, height()+2, Qt::blue); + + delete fbo; + } + +}; + +void tst_QGL::glFBOUseInGLWidget() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform"); + + FBOUseInGLWidget w; + w.resize(100, 100); + w.showNormal(); + + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QVERIFY(w.widgetPainterBeginOk); + QVERIFY(w.fboPainterBeginOk); + +#if defined(Q_OS_QNX) + // glReadPixels reads from the back buffer. On QNX the buffer is not preserved + // after a buffer swap. This is why we have to swap the buffer explicitly before calling + // grabFrameBuffer to retrieve the content of the front buffer + w.swapBuffers(); +#endif + + QImage widgetFB = w.grabFrameBuffer(false); + QImage widgetReference(widgetFB.size(), widgetFB.format()); + widgetReference.fill(0xff0000ff); + QFUZZY_COMPARE_IMAGES(widgetFB, widgetReference); + + QImage fboReference(w.fboImage.size(), w.fboImage.format()); + fboReference.fill(0xffff0000); + QFUZZY_COMPARE_IMAGES(w.fboImage, fboReference); +} + +void tst_QGL::glWidgetReparent() +{ + // Try it as a top-level first: + GLWidget *widget = new GLWidget; + widget->setObjectName(QStringLiteral("glWidget1")); + widget->setGeometry(0, 0, 200, 30); + widget->show(); + + QWidget grandParentWidget; + grandParentWidget.setObjectName(QStringLiteral("grandParentWidget")); + grandParentWidget.setPalette(Qt::blue); + QVBoxLayout grandParentLayout(&grandParentWidget); + + QWidget parentWidget(&grandParentWidget); + parentWidget.setObjectName(QStringLiteral("parentWidget")); + grandParentLayout.addWidget(&parentWidget); + parentWidget.setPalette(Qt::green); + parentWidget.setAutoFillBackground(true); + QVBoxLayout parentLayout(&parentWidget); + + grandParentWidget.setGeometry(0, 100, 200, 200); + grandParentWidget.show(); + + QVERIFY(QTest::qWaitForWindowExposed(widget)); + QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget)); + + QVERIFY(parentWidget.children().count() == 1); // The layout + + // Now both widgets should be created & shown, time to re-parent: + parentLayout.addWidget(widget); + + QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget)); + + QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget + QVERIFY(parentWidget.children().contains(widget)); + QTRY_VERIFY(widget->height() > 30); + + delete widget; + + QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget)); + + QVERIFY(parentWidget.children().count() == 1); // The layout + + // Now do pretty much the same thing, but don't show the + // widget first: + widget = new GLWidget; + widget->setObjectName(QStringLiteral("glWidget2")); + parentLayout.addWidget(widget); + + QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget)); + + QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget + QVERIFY(parentWidget.children().contains(widget)); + QVERIFY(widget->height() > 30); + + delete widget; +} + +class RenderPixmapWidget : public QGLWidget +{ +protected: + void initializeGL() { + // Set some gl state: + QOpenGLContext::currentContext()->functions()->glClearColor(1.0, 0.0, 0.0, 1.0); + } + + void paintGL() { + QOpenGLContext::currentContext()->functions()->glClear(GL_COLOR_BUFFER_BIT); + } +}; + +void tst_QGL::glWidgetRenderPixmap() +{ + RenderPixmapWidget *w = new RenderPixmapWidget; + + QSize pmSize = QSize(100, 100); + QPixmap pm = w->renderPixmap(pmSize.width(), pmSize.height(), false); + + delete w; + + QImage fb = pm.toImage().convertToFormat(QImage::Format_RGB32); + QImage reference(pmSize, QImage::Format_RGB32); + reference.fill(0xffff0000); + + QFUZZY_COMPARE_IMAGES(fb, reference); +} + +class ColormapExtended : public QGLColormap +{ +public: + ColormapExtended() {} + + Qt::HANDLE handle() { return QGLColormap::handle(); } + void setHandle(Qt::HANDLE handle) { QGLColormap::setHandle(handle); } +}; + +void tst_QGL::colormap() +{ + // Check the properties of the default empty colormap. + QGLColormap cmap1; + QVERIFY(cmap1.isEmpty()); + QCOMPARE(cmap1.size(), 0); + QCOMPARE(cmap1.entryRgb(0), QRgb(0)); + QCOMPARE(cmap1.entryRgb(-1), QRgb(0)); + QCOMPARE(cmap1.entryRgb(100), QRgb(0)); + QVERIFY(!cmap1.entryColor(0).isValid()); + QVERIFY(!cmap1.entryColor(-1).isValid()); + QVERIFY(!cmap1.entryColor(100).isValid()); + QCOMPARE(cmap1.find(qRgb(255, 0, 0)), -1); + QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), -1); + + // Set an entry and re-test. + cmap1.setEntry(56, qRgb(255, 0, 0)); + // The colormap is still considered "empty" even though it + // has entries in it now. The isEmpty() method is used to + // detect when the colormap is in use by a GL widget, + // not to detect when it is empty! + QVERIFY(cmap1.isEmpty()); + QCOMPARE(cmap1.size(), 256); + QCOMPARE(cmap1.entryRgb(0), QRgb(0)); + QVERIFY(cmap1.entryColor(0) == QColor(0, 0, 0, 255)); + QVERIFY(cmap1.entryRgb(56) == qRgb(255, 0, 0)); + QVERIFY(cmap1.entryColor(56) == QColor(255, 0, 0, 255)); + QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56); + QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56); + + // Set some more entries. + static QRgb const colors[] = { + qRgb(255, 0, 0), + qRgb(0, 255, 0), + qRgb(255, 255, 255), + qRgb(0, 0, 255), + qRgb(0, 0, 0) + }; + cmap1.setEntry(57, QColor(0, 255, 0)); + cmap1.setEntries(3, colors + 2, 58); + cmap1.setEntries(5, colors, 251); + int idx; + for (idx = 0; idx < 5; ++idx) { + QVERIFY(cmap1.entryRgb(56 + idx) == colors[idx]); + QVERIFY(cmap1.entryColor(56 + idx) == QColor(colors[idx])); + QVERIFY(cmap1.entryRgb(251 + idx) == colors[idx]); + QVERIFY(cmap1.entryColor(251 + idx) == QColor(colors[idx])); + } + QCOMPARE(cmap1.size(), 256); + + // Perform color lookups. + QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56); + QCOMPARE(cmap1.find(qRgb(0, 0, 0)), 60); // Actually finds 0, 0, 0, 255. + QCOMPARE(cmap1.find(qRgba(0, 0, 0, 0)), 0); + QCOMPARE(cmap1.find(qRgb(0, 255, 0)), 57); + QCOMPARE(cmap1.find(qRgb(255, 255, 255)), 58); + QCOMPARE(cmap1.find(qRgb(0, 0, 255)), 59); + QCOMPARE(cmap1.find(qRgb(140, 0, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 140, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 0, 140)), -1); + QCOMPARE(cmap1.find(qRgb(64, 0, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 64, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 0, 64)), -1); + QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 0)), 60); + QCOMPARE(cmap1.findNearest(qRgba(0, 0, 0, 0)), 0); + QCOMPARE(cmap1.findNearest(qRgb(0, 255, 0)), 57); + QCOMPARE(cmap1.findNearest(qRgb(255, 255, 255)), 58); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 255)), 59); + QCOMPARE(cmap1.findNearest(qRgb(140, 0, 0)), 56); + QCOMPARE(cmap1.findNearest(qRgb(0, 140, 0)), 57); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 140)), 59); + QCOMPARE(cmap1.findNearest(qRgb(64, 0, 0)), 0); + QCOMPARE(cmap1.findNearest(qRgb(0, 64, 0)), 0); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 64)), 0); + + // Make some copies of the colormap and check that they are the same. + QGLColormap cmap2(cmap1); + QGLColormap cmap3; + cmap3 = cmap1; + QVERIFY(cmap2.isEmpty()); + QVERIFY(cmap3.isEmpty()); + QCOMPARE(cmap2.size(), 256); + QCOMPARE(cmap3.size(), 256); + for (idx = 0; idx < 256; ++idx) { + QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx)); + QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx)); + } + + // Modify an entry in one of the copies and recheck the original. + cmap2.setEntry(45, qRgb(255, 0, 0)); + for (idx = 0; idx < 256; ++idx) { + if (idx != 45) + QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx)); + else + QCOMPARE(cmap2.entryRgb(45), qRgb(255, 0, 0)); + QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx)); + } + + // Check that setting the handle will cause isEmpty() to work right. + ColormapExtended cmap4; + cmap4.setEntry(56, qRgb(255, 0, 0)); + QVERIFY(cmap4.isEmpty()); + QCOMPARE(cmap4.size(), 256); + cmap4.setHandle(Qt::HANDLE(42)); + QCOMPARE(cmap4.handle(), Qt::HANDLE(42)); + QVERIFY(!cmap4.isEmpty()); + QCOMPARE(cmap4.size(), 256); +} + +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif + +#ifndef GL_RGB16 +#define GL_RGB16 0x8054 +#endif + +void tst_QGL::fboFormat() +{ + // Check the initial conditions. + QGLFramebufferObjectFormat format1; + QCOMPARE(format1.samples(), 0); + QCOMPARE(format1.attachment(), QGLFramebufferObject::NoAttachment); + QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_2D)); + int expectedFormat = +#ifdef QT_OPENGL_ES_2 + GL_RGBA; +#else + QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8; +#endif + QCOMPARE(int(format1.internalTextureFormat()), expectedFormat); + + // Modify the values and re-check. + format1.setSamples(8); + format1.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + format1.setTextureTarget(GL_TEXTURE_3D); + format1.setInternalTextureFormat(GL_RGB16); + QCOMPARE(format1.samples(), 8); + QCOMPARE(format1.attachment(), QGLFramebufferObject::CombinedDepthStencil); + QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D)); + QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16)); + + // Make copies and check that they are the same. + QGLFramebufferObjectFormat format2(format1); + QGLFramebufferObjectFormat format3; + QCOMPARE(format2.samples(), 8); + QCOMPARE(format2.attachment(), QGLFramebufferObject::CombinedDepthStencil); + QCOMPARE(int(format2.textureTarget()), int(GL_TEXTURE_3D)); + QCOMPARE(int(format2.internalTextureFormat()), int(GL_RGB16)); + format3 = format1; + QCOMPARE(format3.samples(), 8); + QCOMPARE(format3.attachment(), QGLFramebufferObject::CombinedDepthStencil); + QCOMPARE(int(format3.textureTarget()), int(GL_TEXTURE_3D)); + QCOMPARE(int(format3.internalTextureFormat()), int(GL_RGB16)); + + // Modify the copies and check that the original is unchanged. + format2.setSamples(9); + format3.setTextureTarget(GL_TEXTURE_2D); + QCOMPARE(format1.samples(), 8); + QCOMPARE(format1.attachment(), QGLFramebufferObject::CombinedDepthStencil); + QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D)); + QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16)); + + // operator== and operator!= for QGLFramebufferObjectFormat. + QGLFramebufferObjectFormat format1c; + QGLFramebufferObjectFormat format2c; + + QCOMPARE(format1c, format2c); + QVERIFY(!(format1c != format2c)); + format1c.setSamples(8); + QVERIFY(!(format1c == format2c)); + QVERIFY(format1c != format2c); + format2c.setSamples(8); + QCOMPARE(format1c, format2c); + QVERIFY(!(format1c != format2c)); + + format1c.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + QVERIFY(!(format1c == format2c)); + QVERIFY(format1c != format2c); + format2c.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + QCOMPARE(format1c, format2c); + QVERIFY(!(format1c != format2c)); + + format1c.setTextureTarget(GL_TEXTURE_3D); + QVERIFY(!(format1c == format2c)); + QVERIFY(format1c != format2c); + format2c.setTextureTarget(GL_TEXTURE_3D); + QCOMPARE(format1c, format2c); + QVERIFY(!(format1c != format2c)); + + format1c.setInternalTextureFormat(GL_RGB16); + QVERIFY(!(format1c == format2c)); + QVERIFY(format1c != format2c); + format2c.setInternalTextureFormat(GL_RGB16); + QCOMPARE(format1c, format2c); + QVERIFY(!(format1c != format2c)); + + QGLFramebufferObjectFormat format3c(format1c); + QGLFramebufferObjectFormat format4c; + QCOMPARE(format1c, format3c); + QVERIFY(!(format1c != format3c)); + format3c.setInternalTextureFormat( +#ifdef QT_OPENGL_ES_2 + GL_RGBA +#else + QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8 +#endif + ); + QVERIFY(!(format1c == format3c)); + QVERIFY(format1c != format3c); + + format4c = format1c; + QCOMPARE(format1c, format4c); + QVERIFY(!(format1c != format4c)); + format4c.setInternalTextureFormat( +#ifdef QT_OPENGL_ES_2 + GL_RGBA +#else + QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8 +#endif + ); + QVERIFY(!(format1c == format4c)); + QVERIFY(format1c != format4c); +} + +void tst_QGL::testDontCrashOnDanglingResources() +{ + // We have a number of Q_GLOBAL_STATICS inside the Qt OpenGL + // module. This test is verify that we don't crash as a result of + // them calling into libgl on application shutdown. + QWidget *widget = new UnclippedWidget(); + widget->show(); + qApp->processEvents(); + widget->hide(); +} + +class ReplaceClippingGLWidget : public QGLWidget +{ +public: + void paint(QPainter *painter) + { + painter->fillRect(rect(), Qt::white); + + QPainterPath path; + path.addRect(0, 0, 100, 100); + path.addRect(50, 50, 100, 100); + + painter->setClipRect(0, 0, 150, 150); + painter->fillPath(path, Qt::red); + + painter->translate(150, 150); + painter->setClipRect(0, 0, 150, 150); + painter->fillPath(path, Qt::red); + } + +protected: + void paintEvent(QPaintEvent*) + { + // clear the stencil with junk + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + funcs->glStencilMask(0xFFFF); + funcs->glClearStencil(0xFFFF); + funcs->glDisable(GL_STENCIL_TEST); + funcs->glDisable(GL_SCISSOR_TEST); + funcs->glClear(GL_STENCIL_BUFFER_BIT); + + QPainter painter(this); + paint(&painter); + } +}; + +void tst_QGL::replaceClipping() +{ + ReplaceClippingGLWidget glw; + glw.resize(300, 300); + glw.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&glw)); + + QImage reference(300, 300, QImage::Format_RGB32); + QPainter referencePainter(&reference); + glw.paint(&referencePainter); + referencePainter.end(); + +#if defined(Q_OS_QNX) + // glReadPixels reads from the back buffer. On QNX the buffer is not preserved + // after a buffer swap. This is why we have to swap the buffer explicitly before calling + // grabFrameBuffer to retrieve the content of the front buffer + glw.swapBuffers(); +#endif + const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32); + + // Sample pixels in a grid pattern which avoids false failures due to + // off-by-one pixel errors on some buggy GL implementations + for (int x = 25; x < reference.width(); x += 50) { + for (int y = 25; y < reference.width(); y += 50) { + QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y)); + } + } +} + +class ClipTestGLWidget : public QGLWidget +{ +public: + void paint(QPainter *painter) + { + painter->fillRect(-1, -1, width()+2, height()+2, Qt::white); + painter->setClipRect(10, 10, width()-20, height()-20); + painter->fillRect(rect(), Qt::cyan); + + painter->save(); + painter->setClipRect(10, 10, 100, 100, Qt::IntersectClip); + + painter->fillRect(rect(), Qt::blue); + + painter->save(); + painter->setClipRect(10, 10, 50, 50, Qt::IntersectClip); + painter->fillRect(rect(), Qt::red); + painter->restore(); + painter->fillRect(0, 0, 40, 40, Qt::white); + painter->save(); + + painter->setClipRect(0, 0, 35, 35, Qt::IntersectClip); + painter->fillRect(rect(), Qt::black); + painter->restore(); + + painter->fillRect(0, 0, 30, 30, Qt::magenta); + + painter->save(); + painter->setClipRect(60, 10, 50, 50, Qt::ReplaceClip); + painter->fillRect(rect(), Qt::green); + painter->restore(); + + painter->restore(); + + painter->translate(100, 100); + + { + QPainterPath path; + path.addRect(10, 10, 100, 100); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + + painter->fillRect(rect(), Qt::blue); + + painter->save(); + { + QPainterPath path; + path.addRect(10, 10, 50, 50); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + painter->fillRect(rect(), Qt::red); + painter->restore(); + painter->fillRect(0, 0, 40, 40, Qt::white); + painter->save(); + + { + QPainterPath path; + path.addRect(0, 0, 35, 35); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + painter->fillRect(rect(), Qt::black); + painter->restore(); + + painter->fillRect(0, 0, 30, 30, Qt::magenta); + + painter->save(); + { + QPainterPath path; + path.addRect(60, 10, 50, 50); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::ReplaceClip); + } + painter->fillRect(rect(), Qt::green); + painter->restore(); + } + +protected: + void paintEvent(QPaintEvent*) + { + QPainter painter(this); + paint(&painter); + } +}; + +void tst_QGL::clipTest() +{ + ClipTestGLWidget glw; + glw.resize(220, 220); + glw.showNormal(); + + QVERIFY(QTest::qWaitForWindowExposed(&glw)); + + QImage reference(glw.size(), QImage::Format_RGB32); + QPainter referencePainter(&reference); + glw.paint(&referencePainter); + referencePainter.end(); + +#if defined(Q_OS_QNX) + // glReadPixels reads from the back buffer. On QNX the buffer is not preserved + // after a buffer swap. This is why we have to swap the buffer explicitly before calling + // grabFrameBuffer to retrieve the content of the front buffer + glw.swapBuffers(); +#endif + const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32); + + // Sample pixels in a grid pattern which avoids false failures due to + // off-by-one pixel errors on some buggy GL implementations + for (int x = 2; x < reference.width(); x += 5) { + for (int y = 2; y < reference.height(); y += 5) { + QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y)); + } + } +} + +void tst_QGL::destroyFBOAfterContext() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform"); + + QGLWidget *glw = new QGLWidget(); + glw->makeCurrent(); + + // No multisample with combined depth/stencil attachment: + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + + // Don't complicate things by using NPOT: + QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat); + + // The handle should be valid until the context is destroyed. + QVERIFY(fbo->handle() != 0); + QVERIFY(fbo->isValid()); + + delete glw; + + // The handle should now be zero. + QVERIFY(!fbo->handle()); + QVERIFY(!fbo->isValid()); + + delete fbo; +} + +#ifdef QT_BUILD_INTERNAL + +class tst_QGLResource +{ +public: + tst_QGLResource(const QGLContext * = 0) {} + ~tst_QGLResource() { ++deletions; } + + static int deletions; +}; + +int tst_QGLResource::deletions = 0; + +#ifdef TODO +Q_GLOBAL_STATIC(QOpenGLContextGroupResource<tst_QGLResource>, qt_shared_test) +#endif //TODO +#endif // QT_BUILD_INTERNAL + +#ifdef QT_BUILD_INTERNAL +void tst_QGL::shareRegister() +{ +#ifdef TODO + // Create a context. + QGLWidget *glw1 = new QGLWidget(); + glw1->makeCurrent(); + + // Nothing should be sharing with glw1's context yet. + QVERIFY(!glw1->isSharing()); + + // Create a guard for the first context. + QOpenGLSharedResourceGuard guard(glw1->context()->contextHandle()); + QCOMPARE(guard.id(), 0); + guard.setId(3); + QCOMPARE(guard.id(), 3); + + // Request a tst_QGLResource object for the first context. + tst_QGLResource *res1 = qt_shared_test()->value(glw1->context()->contextHandle()); + QVERIFY(res1); + QCOMPARE(qt_shared_test()->value(glw1->context()->contextHandle()), res1); + + // Create another context that shares with the first. + QVERIFY(!glw1->isSharing()); + QGLWidget *glw2 = new QGLWidget(0, glw1); + if (!glw2->isSharing()) { + delete glw2; + delete glw1; + QSKIP("Context sharing is not supported"); + } + QVERIFY(glw1->isSharing()); + QVERIFY(glw1->context() != glw2->context()); + + // Check that the first context's resource is also on the second. + QCOMPARE(qt_shared_test()->value(glw1->context()), res1); + QCOMPARE(qt_shared_test()->value(glw2->context()), res1); + + // Guard should still be the same. + QCOMPARE(guard.context(), glw1->context()); + QCOMPARE(guard.id(), 3); + + // Check the sharing relationships. + QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context())); + QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context())); + QVERIFY(!QGLContext::areSharing(0, glw2->context())); + QVERIFY(!QGLContext::areSharing(glw1->context(), 0)); + QVERIFY(!QGLContext::areSharing(0, 0)); + + // Create a third context, not sharing with the others. + QGLWidget *glw3 = new QGLWidget(); + QVERIFY(!glw3->isSharing()); + + // Create a guard on the standalone context. + QGLSharedResourceGuard guard3(glw3->context()); + guard3.setId(5); + + // Request a resource to the third context. + tst_QGLResource *res3 = qt_shared_test()->value(glw3->context()); + QVERIFY(res3); + QCOMPARE(qt_shared_test()->value(glw1->context()), res1); + QCOMPARE(qt_shared_test()->value(glw2->context()), res1); + QCOMPARE(qt_shared_test()->value(glw3->context()), res3); + + // Check the sharing relationships again. + QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context())); + QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context())); + QVERIFY(!QGLContext::areSharing(glw1->context(), glw3->context())); + QVERIFY(!QGLContext::areSharing(glw2->context(), glw3->context())); + QVERIFY(!QGLContext::areSharing(glw3->context(), glw1->context())); + QVERIFY(!QGLContext::areSharing(glw3->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw3->context(), glw3->context())); + QVERIFY(!QGLContext::areSharing(0, glw2->context())); + QVERIFY(!QGLContext::areSharing(glw1->context(), 0)); + QVERIFY(!QGLContext::areSharing(0, glw3->context())); + QVERIFY(!QGLContext::areSharing(glw3->context(), 0)); + QVERIFY(!QGLContext::areSharing(0, 0)); + + // Shared guard should still be the same. + QCOMPARE(guard.context(), glw1->context()); + QCOMPARE(guard.id(), 3); + + // Delete the first context. + delete glw1; + + // The second context should no longer register as sharing. + QVERIFY(!glw2->isSharing()); + + // The first context's resource should transfer to the second context. + QCOMPARE(tst_QGLResource::deletions, 0); + QCOMPARE(qt_shared_test()->value(glw2->context()), res1); + QCOMPARE(qt_shared_test()->value(glw3->context()), res3); + + // Shared guard should now be the second context, with the id the same. + QCOMPARE(guard.context(), glw2->context()); + QCOMPARE(guard.id(), 3); + QCOMPARE(guard3.context(), glw3->context()); + QCOMPARE(guard3.id(), 5); + + // Clean up and check that the resources are properly deleted. + delete glw2; + QCOMPARE(tst_QGLResource::deletions, 1); + delete glw3; + QCOMPARE(tst_QGLResource::deletions, 2); + + // Guards should now be null and the id zero. + QVERIFY(guard.context() == 0); + QVERIFY(guard.id() == 0); + QVERIFY(guard3.context() == 0); + QVERIFY(guard3.id() == 0); +#endif //TODO +} +#endif + +// Tests QGLContext::bindTexture with default options +#ifdef QT_BUILD_INTERNAL +void tst_QGL::qglContextDefaultBindTexture() +{ + QGLWidget w; + w.makeCurrent(); + QGLContext *ctx = const_cast<QGLContext*>(w.context()); + + QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32); + boundImage->fill(0xFFFFFFFF); + QPixmap *boundPixmap = new QPixmap(256, 256); + boundPixmap->fill(Qt::red); + + int startCacheItemCount = QGLTextureCache::instance()->size(); + + GLuint boundImageTextureId = ctx->bindTexture(*boundImage); + GLuint boundPixmapTextureId = ctx->bindTexture(*boundPixmap); + + // Make sure the image & pixmap have been added to the cache: + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + + // Make sure the image & pixmap have the is_cached flag set: + QVERIFY(QImagePixmapCleanupHooks::isImageCached(*boundImage)); + QVERIFY(QImagePixmapCleanupHooks::isPixmapCached(*boundPixmap)); + + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + // Make sure the texture IDs returned are valid: + QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_TRUE)); + QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_TRUE)); + + // Make sure the textures are still valid after we delete the image/pixmap: + // Also check that although the textures are left intact, the cache entries are removed: + delete boundImage; + boundImage = 0; + QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_TRUE)); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + delete boundPixmap; + boundPixmap = 0; + QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_TRUE)); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + + // Finally, make sure QGLContext::deleteTexture deletes the texture IDs: + ctx->deleteTexture(boundImageTextureId); + ctx->deleteTexture(boundPixmapTextureId); + QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_FALSE)); + QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_FALSE)); +} +#endif + +#ifdef QT_BUILD_INTERNAL +void tst_QGL::textureCleanup() +{ + QGLWidget w; + w.resize(200,200); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + w.makeCurrent(); + + // Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache + // when the pixmap cache is cleared + { + int startCacheItemCount = QGLTextureCache::instance()->size(); + QPainter p(&w); + + QPixmap boundPixmap(":designer.png"); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + + p.drawPixmap(0, 0, boundPixmap); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + // Need to call end for the GL2 paint engine to release references to pixmap if using tfp + p.end(); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + // Check that the texture doesn't get removed from the cache when the pixmap is cleared + // as it should still be in the cache: + boundPixmap = QPixmap(); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + QPixmapCache::clear(); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + } + + // Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache + // when they are explicitly removed from the pixmap cache + { + int startCacheItemCount = QGLTextureCache::instance()->size(); + QPainter p(&w); + + QPixmap boundPixmap(128, 128); + QString cacheKey = QString::fromLatin1("myPixmap"); + QPixmapCache::insert(cacheKey, boundPixmap); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + + p.drawPixmap(0, 0, boundPixmap); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + // Need to call end for the GL2 paint engine to release references to pixmap if using tfp + p.end(); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + // Check that the texture doesn't get removed from the cache when the pixmap is cleared + // as it should still be in the cache: + boundPixmap = QPixmap(); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + // Finally, we check that the texture cache entry is removed when we remove the + // pixmap cache entry, which should hold the last reference: + QPixmapCache::remove(cacheKey); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + } + + // Check images & pixmaps are removed from the cache when they are deleted + { + int startCacheItemCount = QGLTextureCache::instance()->size(); + QPainter p(&w); + + QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32); + boundImage->fill(0xFFFFFFFF); + QPixmap *boundPixmap = new QPixmap(256, 256); + boundPixmap->fill(Qt::red); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + + p.drawImage(0, 0, *boundImage); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + p.drawPixmap(0, 0, *boundPixmap); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + + // Need to call end for the GL2 paint engine to release references to pixmap if using tfp + p.end(); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + + delete boundImage; + boundImage = 0; + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + delete boundPixmap; + boundPixmap = 0; + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + } + + // Check images & pixmaps are removed from the cache when they are assigned to + { + int startCacheItemCount = QGLTextureCache::instance()->size(); + QPainter p(&w); + + QImage boundImage(256, 256, QImage::Format_RGB32); + boundImage.fill(0xFFFFFFFF); + QPixmap boundPixmap(256, 256); + boundPixmap.fill(Qt::red); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + + p.drawImage(0, 0, boundImage); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + p.drawPixmap(0, 0, boundPixmap); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + + // Need to call end for the GL2 paint engine to release references to pixmap if using tfp + p.end(); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + + boundImage = QImage(64, 64, QImage::Format_RGB32); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + boundPixmap = QPixmap(64, 64); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + } + + // Check images & pixmaps are removed from the cache when they are modified (detached) + { + int startCacheItemCount = QGLTextureCache::instance()->size(); + QPainter p(&w); + + QImage boundImage(256, 256, QImage::Format_RGB32); + boundImage.fill(0xFFFFFFFF); + QPixmap boundPixmap(256, 256); + boundPixmap.fill(Qt::red); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + + p.drawImage(0, 0, boundImage); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + p.drawPixmap(0, 0, boundPixmap); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + + // Need to call end for the GL2 paint engine to release references to pixmap if using tfp + p.end(); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + + boundImage.fill(0x00000000); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + boundPixmap.fill(Qt::blue); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + } + + // Check that images/pixmaps aren't removed from the cache if a shallow copy has been made + QImage copyOfImage; + QPixmap copyOfPixmap; + int startCacheItemCount = QGLTextureCache::instance()->size(); + { + QPainter p(&w); + + QImage boundImage(256, 256, QImage::Format_RGB32); + boundImage.fill(0xFFFFFFFF); + QPixmap boundPixmap(256, 256); + boundPixmap.fill(Qt::red); + + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); + + p.drawImage(0, 0, boundImage); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + p.drawPixmap(0, 0, boundPixmap); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + + // Need to call end for the GL2 paint engine to release references to pixmap if using tfp + p.end(); + + copyOfImage = boundImage; + copyOfPixmap = boundPixmap; + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + } // boundImage & boundPixmap would have been deleted when they went out of scope + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2); + + copyOfImage = QImage(); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1); + + copyOfPixmap = QPixmap(); + QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount); +} +#endif + +namespace ThreadImages { + +class Producer : public QObject +{ + Q_OBJECT +public: + Producer() + { + startTimer(20); + + QThread *thread = new QThread; + thread->start(); + + connect(this, SIGNAL(destroyed()), thread, SLOT(quit())); + + moveToThread(thread); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + } + +signals: + void imageReady(const QImage &image); + +protected: + void timerEvent(QTimerEvent *) + { + QImage image(256, 256, QImage::Format_RGB32); + QLinearGradient g(0, 0, 0, 256); + g.setColorAt(0, QColor(255, 180, 180)); + g.setColorAt(1, Qt::white); + g.setSpread(QGradient::ReflectSpread); + + QBrush brush(g); + brush.setTransform(QTransform::fromTranslate(0, delta)); + delta += 10; + + QPainter p(&image); + p.fillRect(image.rect(), brush); + + if (images.size() > 10) + images.removeFirst(); + + images.append(image); + + emit imageReady(image); + } + +private: + QList<QImage> images; + int delta; +}; + + +class DisplayWidget : public QGLWidget +{ + Q_OBJECT +public: + DisplayWidget(QWidget *parent) : QGLWidget(parent) {} + void paintEvent(QPaintEvent *) + { + QPainter p(this); + p.drawImage(rect(), m_image); + } + +public slots: + void setImage(const QImage &image) + { + m_image = image; + update(); + } + +private: + QImage m_image; +}; + +class Widget : public QWidget +{ + Q_OBJECT +public: + Widget() + : iterations(0) + , display(0) + , producer(new Producer) + { + startTimer(400); + connect(this, SIGNAL(destroyed()), producer, SLOT(deleteLater())); + } + + int iterations; + +protected: + void timerEvent(QTimerEvent *) + { + ++iterations; + + delete display; + display = new DisplayWidget(this); + connect(producer, SIGNAL(imageReady(QImage)), display, SLOT(setImage(QImage))); + + display->setGeometry(rect()); + display->show(); + } + +private: + DisplayWidget *display; + Producer *producer; +}; + +} + +void tst_QGL::threadImages() +{ + ThreadImages::Widget *widget = new ThreadImages::Widget; + widget->show(); + + while (widget->iterations <= 5) { + qApp->processEvents(); + } + + delete widget; +} + +void tst_QGL::nullRectCrash() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform"); + + QGLWidget glw; + glw.makeCurrent(); + + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + + QGLFramebufferObject *fbo = new QGLFramebufferObject(128, 128, fboFormat); + + QPainter fboPainter(fbo); + + fboPainter.setPen(QPen(QColor(255, 127, 127, 127), 2)); + fboPainter.setBrush(QColor(127, 255, 127, 127)); + fboPainter.drawRect(QRectF()); + + fboPainter.end(); +} + +void tst_QGL::extensions() +{ + QGLWidget glw; + glw.makeCurrent(); + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QVERIFY(ctx); + QOpenGLFunctions *funcs = ctx->functions(); + QVERIFY(funcs); + QSurfaceFormat format = ctx->format(); + +#ifdef QT_BUILD_INTERNAL + QOpenGLExtensions *exts = static_cast<QOpenGLExtensions *>(funcs); + QOpenGLExtensions::OpenGLExtensions allExts = exts->openGLExtensions(); + // Mipmapping is always available in GL2/GLES2+. Verify this. + if (format.majorVersion() >= 2) + QVERIFY(allExts.testFlag(QOpenGLExtensions::GenerateMipmap)); +#endif + + // Now look for some features should always be available in a given version. + QOpenGLFunctions::OpenGLFeatures allFeatures = funcs->openGLFeatures(); + QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Multitexture)); + if (format.majorVersion() >= 2) { + QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Shaders)); + QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Buffers)); + QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Multisample)); + QVERIFY(!ctx->isOpenGLES() || allFeatures.testFlag(QOpenGLFunctions::Framebuffers)); + QVERIFY(allFeatures.testFlag(QOpenGLFunctions::NPOTTextures) + && allFeatures.testFlag(QOpenGLFunctions::NPOTTextureRepeat)); + if (ctx->isOpenGLES()) { + QVERIFY(!allFeatures.testFlag(QOpenGLFunctions::FixedFunctionPipeline)); + QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Framebuffers)); + } + } + if (format.majorVersion() >= 3) + QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Framebuffers)); +} + +QTEST_MAIN(tst_QGL) +#include "tst_qgl.moc" diff --git a/tests/auto/other/lancelot/paintcommands.cpp b/tests/auto/other/lancelot/paintcommands.cpp index c1ff7e7a87..e98df3781e 100644 --- a/tests/auto/other/lancelot/paintcommands.cpp +++ b/tests/auto/other/lancelot/paintcommands.cpp @@ -31,6 +31,7 @@ #include <qfile.h> #include <qfileinfo.h> #include <qpainter.h> +#include <qpainterpath.h> #include <qbitmap.h> #include <qtextstream.h> #include <qtextlayout.h> diff --git a/tests/auto/testlib/selftests/expected_pass.junitxml b/tests/auto/testlib/selftests/expected_pass.junitxml new file mode 100644 index 0000000000..b148593bd0 --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pass.junitxml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<testsuite errors="0" failures="0" tests="5" name="tst_Pass"> + <properties> + <property value="@INSERT_QT_VERSION_HERE@" name="QTestVersion"/> + <property value="@INSERT_QT_VERSION_HERE@" name="QtVersion"/> + <property value="" name="QtBuild"/> + </properties> + <testcase result="pass" name="initTestCase"/> + <testcase result="pass" name="testNumber1"/> + <testcase result="pass" name="testNumber2"/> + <testcase result="pass" name="testNumber3"/> + <testcase result="pass" name="cleanupTestCase"/> + <system-err/> +</testsuite> diff --git a/tests/auto/testlib/selftests/expected_pass.lightxml b/tests/auto/testlib/selftests/expected_pass.lightxml new file mode 100644 index 0000000000..4d344e93cf --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pass.lightxml @@ -0,0 +1,26 @@ +<Environment> + <QtVersion>@INSERT_QT_VERSION_HERE@</QtVersion> + <QtBuild/> + <QTestVersion>@INSERT_QT_VERSION_HERE@</QTestVersion> +</Environment> +<TestFunction name="initTestCase"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="testNumber1"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="testNumber2"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="testNumber3"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="cleanupTestCase"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<Duration msecs="0"/> diff --git a/tests/auto/testlib/selftests/expected_pass.tap b/tests/auto/testlib/selftests/expected_pass.tap new file mode 100644 index 0000000000..a01755dc25 --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pass.tap @@ -0,0 +1,11 @@ +TAP version 13 +# tst_Pass +ok 1 - initTestCase() +ok 2 - testNumber1() +ok 3 - testNumber2() +ok 4 - testNumber3() +ok 5 - cleanupTestCase() +1..5 +# tests 5 +# pass 5 +# fail 0 diff --git a/tests/auto/testlib/selftests/expected_pass.teamcity b/tests/auto/testlib/selftests/expected_pass.teamcity new file mode 100644 index 0000000000..11dc666fb7 --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pass.teamcity @@ -0,0 +1,12 @@ +##teamcity[testSuiteStarted name='tst_Pass' flowId='tst_Pass'] +##teamcity[testStarted name='initTestCase()' flowId='tst_Pass'] +##teamcity[testFinished name='initTestCase()' flowId='tst_Pass'] +##teamcity[testStarted name='testNumber1()' flowId='tst_Pass'] +##teamcity[testFinished name='testNumber1()' flowId='tst_Pass'] +##teamcity[testStarted name='testNumber2()' flowId='tst_Pass'] +##teamcity[testFinished name='testNumber2()' flowId='tst_Pass'] +##teamcity[testStarted name='testNumber3()' flowId='tst_Pass'] +##teamcity[testFinished name='testNumber3()' flowId='tst_Pass'] +##teamcity[testStarted name='cleanupTestCase()' flowId='tst_Pass'] +##teamcity[testFinished name='cleanupTestCase()' flowId='tst_Pass'] +##teamcity[testSuiteFinished name='tst_Pass' flowId='tst_Pass'] diff --git a/tests/auto/testlib/selftests/expected_pass.txt b/tests/auto/testlib/selftests/expected_pass.txt new file mode 100644 index 0000000000..0d7265f970 --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pass.txt @@ -0,0 +1,9 @@ +********* Start testing of tst_Pass ********* +Config: Using QtTest library +PASS : tst_Pass::initTestCase() +PASS : tst_Pass::testNumber1() +PASS : tst_Pass::testNumber2() +PASS : tst_Pass::testNumber3() +PASS : tst_Pass::cleanupTestCase() +Totals: 5 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms +********* Finished testing of tst_Pass ********* diff --git a/tests/auto/testlib/selftests/expected_pass.xml b/tests/auto/testlib/selftests/expected_pass.xml new file mode 100644 index 0000000000..b221cb5411 --- /dev/null +++ b/tests/auto/testlib/selftests/expected_pass.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<TestCase name="tst_Pass"> +<Environment> + <QtVersion>@INSERT_QT_VERSION_HERE@</QtVersion> + <QtBuild/> + <QTestVersion>@INSERT_QT_VERSION_HERE@</QTestVersion> +</Environment> +<TestFunction name="initTestCase"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="testNumber1"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="testNumber2"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="testNumber3"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<TestFunction name="cleanupTestCase"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> +<Duration msecs="0"/> +</TestCase> diff --git a/tests/auto/testlib/selftests/pass/pass.pro b/tests/auto/testlib/selftests/pass/pass.pro new file mode 100644 index 0000000000..dd00a1c744 --- /dev/null +++ b/tests/auto/testlib/selftests/pass/pass.pro @@ -0,0 +1,9 @@ +SOURCES += tst_pass.cpp +QT = core testlib + +macos:CONFIG -= app_bundle +CONFIG -= debug_and_release_target + +TARGET = pass + +include($$QT_SOURCE_TREE/src/testlib/selfcover.pri) diff --git a/tests/auto/testlib/selftests/pass/tst_pass.cpp b/tests/auto/testlib/selftests/pass/tst_pass.cpp new file mode 100644 index 0000000000..6fa5f6a209 --- /dev/null +++ b/tests/auto/testlib/selftests/pass/tst_pass.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QCoreApplication> +#include <QtTest/QtTest> + +class tst_Pass: public QObject +{ + Q_OBJECT + +private slots: + void testNumber1() const; + void testNumber2() const; + void testNumber3() const; +}; + +void tst_Pass::testNumber1() const +{ +} + +void tst_Pass::testNumber2() const +{ +} + +void tst_Pass::testNumber3() const +{ +} + +QTEST_MAIN(tst_Pass) + +#include "tst_pass.moc" diff --git a/tests/auto/testlib/selftests/selftests.pri b/tests/auto/testlib/selftests/selftests.pri index 2cad0b7e17..b124b21981 100644 --- a/tests/auto/testlib/selftests/selftests.pri +++ b/tests/auto/testlib/selftests/selftests.pri @@ -31,6 +31,7 @@ SUBPROGRAMS = \ longstring \ maxwarnings \ multiexec \ + pass \ pairdiagnostics \ printdatatags \ printdatatagswithglobaltags \ diff --git a/tests/auto/testlib/selftests/tst_selftests.cpp b/tests/auto/testlib/selftests/tst_selftests.cpp index be64752ceb..cab6589984 100644 --- a/tests/auto/testlib/selftests/tst_selftests.cpp +++ b/tests/auto/testlib/selftests/tst_selftests.cpp @@ -422,9 +422,9 @@ void tst_Selftests::initTestCase() { QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString())); //Detect the location of the sub programs - QString subProgram = QLatin1String("float/float"); + QString subProgram = QLatin1String("pass/pass"); #if defined(Q_OS_WIN) - subProgram = QLatin1String("float/float.exe"); + subProgram = QLatin1String("pass/pass.exe"); #endif QString testdataDir = QFINDTESTDATA(subProgram); if (testdataDir.lastIndexOf(subProgram) > 0) @@ -487,6 +487,7 @@ void tst_Selftests::runSubTest_data() << "longstring" << "maxwarnings" << "multiexec" + << "pass" << "pairdiagnostics" << "printdatatags" << "printdatatagswithglobaltags" diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp index fe6ef5a00a..1cc793503a 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp @@ -59,6 +59,7 @@ #include <QSharedPointer> #include <float.h> #include <QStyleHints> +#include <QPainterPath> using AbstractGraphicsShapeItemPtr = QSharedPointer<QAbstractGraphicsShapeItem>; using GraphicsItems = QVector<QGraphicsItem *>; diff --git a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp index 2822279190..e7bba010d9 100644 --- a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp @@ -30,6 +30,7 @@ #include <QtTest/QtTest> #include <qgraphicsscene.h> #include <qgraphicsitem.h> +#include <qpainterpath.h> class tst_QGraphicsPixmapItem : public QObject { diff --git a/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp index fbdd38804d..5380dd0daf 100644 --- a/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp @@ -29,6 +29,7 @@ #include <QtTest/QtTest> #include <qgraphicsitem.h> +#include <qpainterpath.h> #include <qpen.h> diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp index cfbe1f96ee..7e5d94da22 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp @@ -37,6 +37,7 @@ #include <QtWidgets/QStyleFactory> #include <QtWidgets/QVBoxLayout> +#include <QtGui/QPainterPath> #include <QtGui/QScreen> #include <QtTest/QtTest> diff --git a/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp b/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp index baccf7bff8..ff01810938 100644 --- a/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp @@ -28,6 +28,7 @@ #include <QtTest/QtTest> +#include <QtGui/QPainterPath> #include <QtWidgets/qgraphicsscene.h> #include <private/qgraphicsscenebsptreeindex_p.h> #include <private/qgraphicssceneindex_p.h> diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp index bd470dbcfc..297e83421b 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp @@ -30,6 +30,7 @@ #include <QtCore/QRectF> #include <QtGui/QTransform> #include <QtGui/QGuiApplication> +#include <QtGui/QPainterPath> #include <QtGui/QScreen> #include <QtWidgets/QStyle> #include <QtWidgets/QStyleFactory> diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 1f2060942b..2483d7b5bb 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -58,6 +58,7 @@ #include <qtoolbutton.h> #include <QtCore/qoperatingsystemversion.h> #include <QtGui/qpaintengine.h> +#include <QtGui/qpainterpath.h> #include <QtGui/qbackingstore.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpa/qplatformwindow.h> diff --git a/tests/benchmarks/gui/painting/qpainter/tst_qpainter.cpp b/tests/benchmarks/gui/painting/qpainter/tst_qpainter.cpp index 485306a8ac..82804a0977 100644 --- a/tests/benchmarks/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/benchmarks/gui/painting/qpainter/tst_qpainter.cpp @@ -28,6 +28,7 @@ #include <qtest.h> #include <QPainter> +#include <QPainterPath> #include <QPixmap> #include <QImage> #include <QPaintEngine> diff --git a/tests/benchmarks/gui/painting/qtbench/benchmarktests.h b/tests/benchmarks/gui/painting/qtbench/benchmarktests.h index b31b6823aa..6d388fcaad 100644 --- a/tests/benchmarks/gui/painting/qtbench/benchmarktests.h +++ b/tests/benchmarks/gui/painting/qtbench/benchmarktests.h @@ -37,6 +37,7 @@ #include <QDebug> #include <QStaticText> #include <QPainter> +#include <QPainterPath> #include <QRandomGenerator> class Benchmark diff --git a/tests/benchmarks/gui/painting/qtransform/tst_qtransform.cpp b/tests/benchmarks/gui/painting/qtransform/tst_qtransform.cpp index 12e85d4842..d28f9502d7 100644 --- a/tests/benchmarks/gui/painting/qtransform/tst_qtransform.cpp +++ b/tests/benchmarks/gui/painting/qtransform/tst_qtransform.cpp @@ -28,6 +28,7 @@ #include <qtest.h> #include <QTransform> +#include <QPainterPath> class tst_QTransform : public QObject { |