diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2020-03-11 01:00:56 +0100 |
---|---|---|
committer | Sona Kurazyan <sona.kurazyan@qt.io> | 2020-03-11 15:39:56 +0100 |
commit | 3d315860cdf13fe2adf78c8eea07aea10444ed72 (patch) | |
tree | b5a7daa8534b8dadc3abfd665655af3a2450af76 /src | |
parent | cd80f347cf5d40938df7c10d1f6dac3d8c81ff95 (diff) | |
parent | 865afac25036d58b18794384e37d42931b2164c5 (diff) |
Merge "Merge remote-tracking branch 'origin/5.15' into dev"
Diffstat (limited to 'src')
77 files changed, 1790 insertions, 993 deletions
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> |