diff options
67 files changed, 2715 insertions, 1641 deletions
diff --git a/configure.bat b/configure.bat index b34e146f5f..163e296874 100644 --- a/configure.bat +++ b/configure.bat @@ -261,7 +261,7 @@ if "%tmpl%" == "win32" ( echo QMAKESPEC = %PLATFORM%>> Makefile ) else ( echo QMAKESPEC = $^(SOURCE_PATH^)/mkspecs/%PLATFORM%>> Makefile - echo CONFIG_CXXFLAGS = -std=c++11 -ffunction-sections>> Makefile + echo CONFIG_CXXFLAGS = -std=c++17 -ffunction-sections>> Makefile echo CONFIG_LFLAGS = -Wl,--gc-sections>> Makefile type "%QTSRC%\qmake\Makefile.unix.win32" >> Makefile type "%QTSRC%\qmake\Makefile.unix.mingw" >> Makefile diff --git a/examples/widgets/animation/easing/window.cpp b/examples/widgets/animation/easing/window.cpp index 378af07535..51d252e52b 100644 --- a/examples/widgets/animation/easing/window.cpp +++ b/examples/widgets/animation/easing/window.cpp @@ -67,7 +67,7 @@ Window::Window(QWidget *parent) connect(m_ui.easingCurvePicker, &QListWidget::currentRowChanged, this, &Window::curveChanged); - connect(m_ui.buttonGroup, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), + connect(m_ui.buttonGroup, &QButtonGroup::buttonClicked, this, &Window::pathChanged); connect(m_ui.periodSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &Window::periodChanged); diff --git a/examples/widgets/widgets/icons/mainwindow.cpp b/examples/widgets/widgets/icons/mainwindow.cpp index 4f990f7320..928470b054 100644 --- a/examples/widgets/widgets/icons/mainwindow.cpp +++ b/examples/widgets/widgets/icons/mainwindow.cpp @@ -172,15 +172,16 @@ void MainWindow::changeStyle(bool checked) //! [4] //! [5] -void MainWindow::changeSize(int id, bool checked) +void MainWindow::changeSize(QAbstractButton *button, bool checked) { if (!checked) return; - const bool other = id == int(OtherSize); + const int index = sizeButtonGroup->id(button); + const bool other = index == int(OtherSize); const int extent = other ? otherSpinBox->value() - : QApplication::style()->pixelMetric(static_cast<QStyle::PixelMetric>(id)); + : QApplication::style()->pixelMetric(static_cast<QStyle::PixelMetric>(index)); previewArea->setSize(QSize(extent, extent)); otherSpinBox->setEnabled(other); @@ -188,7 +189,7 @@ void MainWindow::changeSize(int id, bool checked) void MainWindow::triggerChangeSize() { - changeSize(sizeButtonGroup->checkedId(), true); + changeSize(sizeButtonGroup->checkedButton(), true); } //! [5] @@ -372,7 +373,7 @@ QWidget *MainWindow::createIconSizeGroupBox() sizeButtonGroup = new QButtonGroup(this); sizeButtonGroup->setExclusive(true); - connect(sizeButtonGroup, QOverload<int, bool>::of(&QButtonGroup::buttonToggled), + connect(sizeButtonGroup, &QButtonGroup::buttonToggled, this, &MainWindow::changeSize); QRadioButton *smallRadioButton = new QRadioButton; diff --git a/examples/widgets/widgets/icons/mainwindow.h b/examples/widgets/widgets/icons/mainwindow.h index c67d828cab..9250711ecc 100644 --- a/examples/widgets/widgets/icons/mainwindow.h +++ b/examples/widgets/widgets/icons/mainwindow.h @@ -62,6 +62,7 @@ class QActionGroup; class QLabel; class QButtonGroup; class QTableWidget; +class QAbstractButton; QT_END_NAMESPACE class IconPreviewArea; class IconSizeSpinBox; @@ -81,7 +82,7 @@ public: private slots: void about(); void changeStyle(bool checked); - void changeSize(int, bool); + void changeSize(QAbstractButton *button, bool); void triggerChangeSize(); void changeIcon(); void addSampleImages(); diff --git a/qmake/library/proitems.h b/qmake/library/proitems.h index 4569d7c3ff..01370959c7 100644 --- a/qmake/library/proitems.h +++ b/qmake/library/proitems.h @@ -35,6 +35,7 @@ #include <qstring.h> #include <qvector.h> #include <qhash.h> +#include <qmap.h> QT_BEGIN_NAMESPACE @@ -316,7 +317,7 @@ Q_DECLARE_TYPEINFO(ProStringList, Q_MOVABLE_TYPE); inline ProStringList operator+(const ProStringList &one, const ProStringList &two) { ProStringList ret = one; ret += two; return ret; } -typedef QHash<ProKey, ProStringList> ProValueMap; +typedef QMap<ProKey, ProStringList> ProValueMap; // These token definitions affect both ProFileEvaluator and ProWriter enum ProToken { diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 7e3c01d461..4ac26e3048 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -972,7 +972,7 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok) const QVariantMap *map = v_cast<QVariantMap>(d); const auto end = map->end(); for (auto it = map->begin(); it != end; ++it) - static_cast<QMultiHash<QString, QVariant> *>(hash)->insert(it.key(), it.value()); + hash->insert(it.key(), it.value()); #ifndef QT_BOOTSTRAPPED } else if (d->type == QMetaType::QCborValue) { if (!v_cast<QCborValue>(d)->isMap()) diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 353bfa7ce7..a09a70548c 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -818,7 +818,7 @@ namespace QtPrivate { QVariantHash l; l.reserve(iter.size()); for (QAssociativeIterable::const_iterator it = iter.begin(), end = iter.end(); it != end; ++it) - static_cast<QMultiHash<QString, QVariant> &>(l).insert(it.key().toString(), it.value()); + l.insert(it.key().toString(), it.value()); return l; } return QVariantValueHelper<QVariantHash>::invoke(v); diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h index b7c16018d5..270267e29e 100644 --- a/src/corelib/serialization/qdatastream.h +++ b/src/corelib/serialization/qdatastream.h @@ -296,10 +296,7 @@ QDataStream &readAssociativeContainer(QDataStream &s, Container &c) c.clear(); break; } -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - c.insertMulti(k, t); -QT_WARNING_POP + c.insert(k, t); } return s; @@ -319,19 +316,20 @@ template <typename Container> QDataStream &writeAssociativeContainer(QDataStream &s, const Container &c) { s << quint32(c.size()); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) - // Deserialization should occur in the reverse order. - // Otherwise, value() will return the least recently inserted - // value instead of the most recently inserted one. - auto it = c.constEnd(); - auto begin = c.constBegin(); - while (it != begin) { - QT_WARNING_PUSH - QT_WARNING_DISABLE_DEPRECATED - --it; - QT_WARNING_POP + auto it = c.constBegin(); + auto end = c.constEnd(); + while (it != end) { s << it.key() << it.value(); -#else + ++it; + } + + return s; +} + +template <typename Container> +QDataStream &writeAssociativeMultiContainer(QDataStream &s, const Container &c) +{ + s << quint32(c.size()); auto it = c.constBegin(); auto end = c.constEnd(); while (it != end) { @@ -343,7 +341,6 @@ QDataStream &writeAssociativeContainer(QDataStream &s, const Container &c) auto next = std::next(rangeStart, i); s << next.key() << next.value(); } -#endif } return s; @@ -440,12 +437,25 @@ inline QDataStream &operator>>(QDataStream &s, QHash<Key, T> &hash) } template <class Key, class T> + inline QDataStream &operator<<(QDataStream &s, const QHash<Key, T> &hash) { return QtPrivate::writeAssociativeContainer(s, hash); } template <class Key, class T> +inline QDataStream &operator>>(QDataStream &s, QMultiHash<Key, T> &hash) +{ + return QtPrivate::readAssociativeContainer(s, hash); +} + +template <class Key, class T> +inline QDataStream &operator<<(QDataStream &s, const QMultiHash<Key, T> &hash) +{ + return QtPrivate::writeAssociativeMultiContainer(s, hash); +} + +template <class Key, class T> inline QDataStream &operator>>(QDataStream &s, QMap<Key, T> &map) { return QtPrivate::readAssociativeContainer(s, map); @@ -457,6 +467,18 @@ inline QDataStream &operator<<(QDataStream &s, const QMap<Key, T> &map) return QtPrivate::writeAssociativeContainer(s, map); } +template <class Key, class T> +inline QDataStream &operator>>(QDataStream &s, QMultiMap<Key, T> &map) +{ + return QtPrivate::readAssociativeContainer(s, map); +} + +template <class Key, class T> +inline QDataStream &operator<<(QDataStream &s, const QMultiMap<Key, T> &map) +{ + return QtPrivate::writeAssociativeMultiContainer(s, map); +} + #ifndef QT_NO_DATASTREAM template <class T1, class T2> inline QDataStream& operator>>(QDataStream& s, QPair<T1, T2>& p) diff --git a/src/corelib/tools/qversionnumber.h b/src/corelib/tools/qversionnumber.h index 9fd4242859..03a27548b8 100644 --- a/src/corelib/tools/qversionnumber.h +++ b/src/corelib/tools/qversionnumber.h @@ -344,30 +344,30 @@ public: if_valid_segment_type<Minor> = true> static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion) { - Q_ASSERT(isValidSegment(majorVersion)); - Q_ASSERT(isValidSegment(minorVersion)); - return QTypeRevision(quint8(majorVersion), quint8(minorVersion)); + return Q_ASSERT(isValidSegment(majorVersion)), + Q_ASSERT(isValidSegment(minorVersion)), + QTypeRevision(quint8(majorVersion), quint8(minorVersion)); } template<typename Major, if_valid_segment_type<Major> = true> static constexpr QTypeRevision fromMajorVersion(Major majorVersion) { - Q_ASSERT(isValidSegment(majorVersion)); - return QTypeRevision(quint8(majorVersion), SegmentUnknown); + return Q_ASSERT(isValidSegment(majorVersion)), + QTypeRevision(quint8(majorVersion), SegmentUnknown); } template<typename Minor, if_valid_segment_type<Minor> = true> static constexpr QTypeRevision fromMinorVersion(Minor minorVersion) { - Q_ASSERT(isValidSegment(minorVersion)); - return QTypeRevision(SegmentUnknown, quint8(minorVersion)); + return Q_ASSERT(isValidSegment(minorVersion)), + QTypeRevision(SegmentUnknown, quint8(minorVersion)); } template<typename Integer, if_valid_value_type<Integer> = true> static constexpr QTypeRevision fromEncodedVersion(Integer value) { - Q_ASSERT((value & ~Integer(0xffff)) == Integer(0)); - return QTypeRevision((value & Integer(0xff00)) >> 8, value & Integer(0xff)); + return Q_ASSERT((value & ~Integer(0xffff)) == Integer(0)), + QTypeRevision((value & Integer(0xff00)) >> 8, value & Integer(0xff)); } static constexpr QTypeRevision zero() { return QTypeRevision(0, 0); } @@ -416,6 +416,42 @@ inline constexpr bool operator!=(QTypeRevision lhs, QTypeRevision rhs) return lhs.toEncodedVersion<quint16>() != rhs.toEncodedVersion<quint16>(); } +inline constexpr bool operator<(QTypeRevision lhs, QTypeRevision rhs) +{ + return (!lhs.hasMajorVersion() && rhs.hasMajorVersion()) + // non-0 major > unspecified major > major 0 + ? rhs.majorVersion() != 0 + : ((lhs.hasMajorVersion() && !rhs.hasMajorVersion()) + // major 0 < unspecified major < non-0 major + ? lhs.majorVersion() == 0 + : (lhs.majorVersion() != rhs.majorVersion() + // both majors specified and non-0 + ? lhs.majorVersion() < rhs.majorVersion() + : ((!lhs.hasMinorVersion() && rhs.hasMinorVersion()) + // non-0 minor > unspecified minor > minor 0 + ? rhs.minorVersion() != 0 + : ((lhs.hasMinorVersion() && !rhs.hasMinorVersion()) + // minor 0 < unspecified minor < non-0 minor + ? lhs.minorVersion() == 0 + // both minors specified and non-0 + : lhs.minorVersion() < rhs.minorVersion())))); +} + +inline constexpr bool operator>(QTypeRevision lhs, QTypeRevision rhs) +{ + return lhs != rhs && !(lhs < rhs); +} + +inline constexpr bool operator<=(QTypeRevision lhs, QTypeRevision rhs) +{ + return lhs == rhs || lhs < rhs; +} + +inline constexpr bool operator>=(QTypeRevision lhs, QTypeRevision rhs) +{ + return lhs == rhs || !(lhs < rhs); +} + Q_STATIC_ASSERT(sizeof(QTypeRevision) == 2); Q_DECLARE_TYPEINFO(QTypeRevision, Q_MOVABLE_TYPE); diff --git a/src/dbus/qdbusargument.cpp b/src/dbus/qdbusargument.cpp index 5a0f0f013b..7e1d847982 100644 --- a/src/dbus/qdbusargument.cpp +++ b/src/dbus/qdbusargument.cpp @@ -908,9 +908,10 @@ void QDBusArgument::endArray() \snippet code/src_qdbus_qdbusargument.cpp 7 - If the type you want to marshall is a QMap or QHash, you need not - declare an \c{operator<<} function for it, since Qt D-Bus provides - generic templates to do the job of marshalling the data. + You usually don't need to provide an \c{operator<<} or \c{operator>>} + function for associative containers such as QHash or std::map, + since Qt D-Bus provides generic templates to do the job of marshalling + the data. \sa endMap(), beginStructure(), beginArray(), beginMapEntry() */ diff --git a/src/dbus/qdbusargument.h b/src/dbus/qdbusargument.h index 339f8c5dc8..f388a65bed 100644 --- a/src/dbus/qdbusargument.h +++ b/src/dbus/qdbusargument.h @@ -224,7 +224,8 @@ Q_DBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QLineF &li Q_DBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QLineF &line); #endif -template<template <typename> class Container, typename T> +template<template <typename> class Container, typename T, + typename = typename Container<T>::iterator> inline QDBusArgument &operator<<(QDBusArgument &arg, const Container<T> &list) { int id = qMetaTypeId<T>(); @@ -237,7 +238,8 @@ inline QDBusArgument &operator<<(QDBusArgument &arg, const Container<T> &list) return arg; } -template<template <typename> class Container, typename T> +template<template <typename> class Container, typename T, + typename = typename Container<T>::iterator> inline const QDBusArgument &operator>>(const QDBusArgument &arg, Container<T> &list) { arg.beginArray(); @@ -252,35 +254,6 @@ inline const QDBusArgument &operator>>(const QDBusArgument &arg, Container<T> &l return arg; } -// QList specializations -template<typename T> -inline QDBusArgument &operator<<(QDBusArgument &arg, const QList<T> &list) -{ - int id = qMetaTypeId<T>(); - arg.beginArray(id); - typename QList<T>::ConstIterator it = list.constBegin(); - typename QList<T>::ConstIterator end = list.constEnd(); - for ( ; it != end; ++it) - arg << *it; - arg.endArray(); - return arg; -} - -template<typename T> -inline const QDBusArgument &operator>>(const QDBusArgument &arg, QList<T> &list) -{ - arg.beginArray(); - list.clear(); - while (!arg.atEnd()) { - T item; - arg >> item; - list.push_back(item); - } - arg.endArray(); - - return arg; -} - inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantList &list) { int id = qMetaTypeId<QDBusVariant>(); @@ -293,15 +266,16 @@ inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantList &list) return arg; } -// QMap specializations -template<typename Key, typename T> -inline QDBusArgument &operator<<(QDBusArgument &arg, const QMap<Key, T> &map) +// Specializations for associative containers +template <template <typename, typename> class Container, typename Key, typename T, + QtPrivate::IfAssociativeIteratorHasKeyAndValue<typename Container<Key, T>::iterator> = true> +inline QDBusArgument &operator<<(QDBusArgument &arg, const Container<Key, T> &map) { int kid = qMetaTypeId<Key>(); int vid = qMetaTypeId<T>(); arg.beginMap(kid, vid); - typename QMap<Key, T>::ConstIterator it = map.constBegin(); - typename QMap<Key, T>::ConstIterator end = map.constEnd(); + auto it = map.begin(); + auto end = map.end(); for ( ; it != end; ++it) { arg.beginMapEntry(); arg << it.key() << it.value(); @@ -311,8 +285,27 @@ inline QDBusArgument &operator<<(QDBusArgument &arg, const QMap<Key, T> &map) return arg; } -template<typename Key, typename T> -inline const QDBusArgument &operator>>(const QDBusArgument &arg, QMap<Key, T> &map) +template <template <typename, typename> class Container, typename Key, typename T, + QtPrivate::IfAssociativeIteratorHasFirstAndSecond<typename Container<Key, T>::iterator> = true> +inline QDBusArgument &operator<<(QDBusArgument &arg, const Container<Key, T> &map) +{ + int kid = qMetaTypeId<Key>(); + int vid = qMetaTypeId<T>(); + arg.beginMap(kid, vid); + auto it = map.begin(); + auto end = map.end(); + for ( ; it != end; ++it) { + arg.beginMapEntry(); + arg << it->first << it->second; + arg.endMapEntry(); + } + arg.endMap(); + return arg; +} + +template <template <typename, typename> class Container, typename Key, typename T, + typename = typename Container<Key, T>::iterator> +inline const QDBusArgument &operator>>(const QDBusArgument &arg, Container<Key, T> &map) { arg.beginMap(); map.clear(); @@ -321,7 +314,7 @@ inline const QDBusArgument &operator>>(const QDBusArgument &arg, QMap<Key, T> &m T value; arg.beginMapEntry(); arg >> key >> value; - static_cast<QMultiMap<Key, T> &>(map).insert(key, value); + map.insert(key, value); arg.endMapEntry(); } arg.endMap(); @@ -342,41 +335,6 @@ inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantMap &map) return arg; } -// QHash specializations -template<typename Key, typename T> -inline QDBusArgument &operator<<(QDBusArgument &arg, const QHash<Key, T> &map) -{ - int kid = qMetaTypeId<Key>(); - int vid = qMetaTypeId<T>(); - arg.beginMap(kid, vid); - typename QHash<Key, T>::ConstIterator it = map.constBegin(); - typename QHash<Key, T>::ConstIterator end = map.constEnd(); - for ( ; it != end; ++it) { - arg.beginMapEntry(); - arg << it.key() << it.value(); - arg.endMapEntry(); - } - arg.endMap(); - return arg; -} - -template<typename Key, typename T> -inline const QDBusArgument &operator>>(const QDBusArgument &arg, QHash<Key, T> &map) -{ - arg.beginMap(); - map.clear(); - while (!arg.atEnd()) { - Key key; - T value; - arg.beginMapEntry(); - arg >> key >> value; - static_cast<QMultiHash<Key, T> &>(map).insert(key, value); - arg.endMapEntry(); - } - arg.endMap(); - return arg; -} - inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantHash &map) { arg.beginMap(QMetaType::QString, qMetaTypeId<QDBusVariant>()); diff --git a/src/gui/configure.json b/src/gui/configure.json index 469473af22..8bc3e8dc0c 100644 --- a/src/gui/configure.json +++ b/src/gui/configure.json @@ -189,6 +189,20 @@ "-ldwrite" ] }, + "dwrite_3": { + "label": "DirectWrite 3", + "test": { + "main": [ + "IUnknown *factory = 0;", + "DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3),", + " &factory);" + ] + }, + "headers": "dwrite_3.h", + "sources": [ + "-ldwrite" + ] + }, "drm": { "label": "KMS", "test": { @@ -1145,6 +1159,12 @@ "condition": "libs.dwrite_1", "output": [ "privateFeature" ] }, + "directwrite3": { + "label": "DirectWrite 3", + "emitIf": "config.win32", + "condition": "features.directwrite1 && libs.dwrite_3", + "output": [ "privateFeature" ] + }, "directwrite2": { "label": "DirectWrite 2", "emitIf": "config.win32", diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index a723ca6e7f..1f8d8d21e9 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -613,6 +613,13 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME \li \c {dpiawareness=[0|1|2} Sets the DPI awareness of the process (see \l{High DPI Displays}, since Qt 5.4). \li \c {fontengine=freetype}, uses the FreeType font engine. + \li \c {fontengine=directwrite}, uses the experimental DirectWrite + font database and defaults to using the DirectWrite font + engine (which is otherwise only used for some font types + or font properties.) This affects font selection and aims + to provide font naming more consistent with other platforms, + but does not support all font formats, such as Postscript + Type-1 or Microsoft FNT fonts. \li \c {menus=[native|none]}, controls the use of native menus. Native menus are implemented using Win32 API and are simpler than diff --git a/src/gui/kernel/qplatformgraphicsbufferhelper.h b/src/gui/kernel/qplatformgraphicsbufferhelper.h index bfe61713d4..36afd4877b 100644 --- a/src/gui/kernel/qplatformgraphicsbufferhelper.h +++ b/src/gui/kernel/qplatformgraphicsbufferhelper.h @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE namespace QPlatformGraphicsBufferHelper { - bool lockAndBindToTexture(QPlatformGraphicsBuffer *graphicsBuffer, bool *swizzleRandB, bool *premultipliedB, const QRect &rect = QRect()); + Q_GUI_EXPORT bool lockAndBindToTexture(QPlatformGraphicsBuffer *graphicsBuffer, bool *swizzleRandB, bool *premultipliedB, const QRect &rect = QRect()); bool bindSWToTexture(const QPlatformGraphicsBuffer *graphicsBuffer, bool *swizzleRandB = nullptr, bool *premultipliedB = nullptr, const QRect &rect = QRect()); } diff --git a/src/gui/opengl/opengl.pri b/src/gui/opengl/opengl.pri index f1e2019417..8d91a9e4b2 100644 --- a/src/gui/opengl/opengl.pri +++ b/src/gui/opengl/opengl.pri @@ -16,7 +16,6 @@ qtConfig(opengl) { opengl/qopenglversionfunctions.h \ opengl/qopenglversionfunctionsfactory_p.h \ opengl/qopenglvertexarrayobject.h \ - opengl/qopengltextureblitter.h \ opengl/qopenglextrafunctions.h \ opengl/qopenglprogrambinarycache_p.h @@ -28,7 +27,6 @@ qtConfig(opengl) { opengl/qopenglversionfunctions.cpp \ opengl/qopenglversionfunctionsfactory.cpp \ opengl/qopenglvertexarrayobject.cpp \ - opengl/qopengltextureblitter.cpp \ opengl/qopenglprogrambinarycache.cpp !qtConfig(opengles2) { diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 390147463d..8aedf2c34d 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -96,10 +96,15 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QTextItem::RenderFlags flags, qreal width, const QTextCharFormat &charFormat); // Helper function to calculate left most position, width and flags for decoration drawing -Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray, - const QFixedPoint *positions, int glyphCount, - QFontEngine *fontEngine, const QFont &font, - const QTextCharFormat &charFormat); +static void qt_draw_decoration_for_glyphs(QPainter *painter, + const QPointF &decorationPosition, + const glyph_t *glyphArray, + const QFixedPoint *positions, + int glyphCount, + QFontEngine *fontEngine, + bool underline, + bool overline, + bool strikeOut); static inline QGradient::CoordinateMode coordinateMode(const QBrush &brush) { @@ -5607,40 +5612,31 @@ void QPainter::drawGlyphRun(const QPointF &position, const QGlyphRun &glyphRun) fixedPointPositions[i] = QFixedPoint::fromPointF(processedPosition); } - d->drawGlyphs(glyphIndexes, fixedPointPositions.data(), count, fontD->fontEngine, - glyphRun.overline(), glyphRun.underline(), glyphRun.strikeOut()); + d->drawGlyphs(engineRequiresPretransformedGlyphPositions + ? d->state->transform().map(position) + : position, + glyphIndexes, + fixedPointPositions.data(), + count, + fontD->fontEngine, + glyphRun.overline(), + glyphRun.underline(), + glyphRun.strikeOut()); } -void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, QFixedPoint *positions, +void QPainterPrivate::drawGlyphs(const QPointF &decorationPosition, + const quint32 *glyphArray, + QFixedPoint *positions, int glyphCount, - QFontEngine *fontEngine, bool overline, bool underline, + QFontEngine *fontEngine, + bool overline, + bool underline, bool strikeOut) { Q_Q(QPainter); updateState(state); - QFixed leftMost; - QFixed rightMost; - QFixed baseLine; - for (int i=0; i<glyphCount; ++i) { - glyph_metrics_t gm = fontEngine->boundingBox(glyphArray[i]); - if (i == 0 || leftMost > positions[i].x) - leftMost = positions[i].x; - - // We don't support glyphs that do not share a common baseline. If this turns out to - // be a relevant use case, then we need to find clusters of glyphs that share a baseline - // and do a drawTextItemDecorations call per cluster. - if (i == 0 || baseLine < positions[i].y) - baseLine = positions[i].y; - - // We use the advance rather than the actual bounds to match the algorithm in drawText() - if (i == 0 || rightMost < positions[i].x + gm.xoff) - rightMost = positions[i].x + gm.xoff; - } - - QFixed width = rightMost - leftMost; - if (extended != nullptr && state->matrix.isAffine()) { QStaticTextItem staticTextItem; staticTextItem.color = state->pen.color(); @@ -5674,21 +5670,15 @@ void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, QFixedPoint *positio engine->drawTextItem(QPointF(0, 0), textItem); } - QTextItemInt::RenderFlags flags; - if (underline) - flags |= QTextItemInt::Underline; - if (overline) - flags |= QTextItemInt::Overline; - if (strikeOut) - flags |= QTextItemInt::StrikeOut; - - drawTextItemDecoration(q, QPointF(leftMost.toReal(), baseLine.toReal()), - fontEngine, - nullptr, // textEngine - (underline - ? QTextCharFormat::SingleUnderline - : QTextCharFormat::NoUnderline), - flags, width.toReal(), QTextCharFormat()); + qt_draw_decoration_for_glyphs(q, + decorationPosition, + glyphArray, + positions, + glyphCount, + fontEngine, + underline, + overline, + strikeOut); } #endif // QT_NO_RAWFONT @@ -5868,9 +5858,15 @@ void QPainter::drawStaticText(const QPointF &topLeftPosition, const QStaticText } d->extended->drawStaticTextItem(item); - qt_draw_decoration_for_glyphs(this, item->glyphs, item->glyphPositions, - item->numGlyphs, item->fontEngine(), staticText_d->font, - QTextCharFormat()); + qt_draw_decoration_for_glyphs(this, + topLeftPosition, + item->glyphs, + item->glyphPositions, + item->numGlyphs, + item->fontEngine(), + staticText_d->font.underline(), + staticText_d->font.overline(), + staticText_d->font.strikeOut()); } if (currentColor != oldPen.color()) setPen(oldPen); @@ -6375,49 +6371,44 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const painter->setRenderHint(QPainter::Qt4CompatiblePainting); } -Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray, - const QFixedPoint *positions, int glyphCount, - QFontEngine *fontEngine, const QFont &font, - const QTextCharFormat &charFormat) +static void qt_draw_decoration_for_glyphs(QPainter *painter, + const QPointF &decorationPosition, + const glyph_t *glyphArray, + const QFixedPoint *positions, + int glyphCount, + QFontEngine *fontEngine, + bool underline, + bool overline, + bool strikeOut) { - if (!(font.underline() || font.strikeOut() || font.overline())) + if (!underline && !overline && !strikeOut) return; - QFixed leftMost; - QFixed rightMost; - QFixed baseLine; - for (int i=0; i<glyphCount; ++i) { - glyph_metrics_t gm = fontEngine->boundingBox(glyphArray[i]); - if (i == 0 || leftMost > positions[i].x) - leftMost = positions[i].x; - - // We don't support glyphs that do not share a common baseline. If this turns out to - // be a relevant use case, then we need to find clusters of glyphs that share a baseline - // and do a drawTextItemDecoration call per cluster. - if (i == 0 || baseLine < positions[i].y) - baseLine = positions[i].y; - - // We use the advance rather than the actual bounds to match the algorithm in drawText() - if (i == 0 || rightMost < positions[i].x + gm.xoff) - rightMost = positions[i].x + gm.xoff; - } - - QFixed width = rightMost - leftMost; QTextItem::RenderFlags flags; - - if (font.underline()) + if (underline) flags |= QTextItem::Underline; - if (font.overline()) + if (overline) flags |= QTextItem::Overline; - if (font.strikeOut()) + if (strikeOut) flags |= QTextItem::StrikeOut; - drawTextItemDecoration(painter, QPointF(leftMost.toReal(), baseLine.toReal()), + bool rtl = positions[glyphCount - 1].x < positions[0].x; + QFixed baseline = positions[0].y; + glyph_metrics_t gm = fontEngine->boundingBox(glyphArray[rtl ? 0 : glyphCount - 1]); + + qreal width = rtl + ? (positions[0].x + gm.xoff - positions[glyphCount - 1].x).toReal() + : (positions[glyphCount - 1].x + gm.xoff - positions[0].x).toReal(); + + drawTextItemDecoration(painter, + QPointF(decorationPosition.x(), baseline.toReal()), fontEngine, nullptr, // textEngine - font.underline() ? QTextCharFormat::SingleUnderline - : QTextCharFormat::NoUnderline, flags, - width.toReal(), charFormat); + underline ? QTextCharFormat::SingleUnderline + : QTextCharFormat::NoUnderline, + flags, + width, + QTextCharFormat()); } void QPainter::drawTextItem(const QPointF &p, const QTextItem &ti) diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h index dafd6e33be..870381d48a 100644 --- a/src/gui/painting/qpainter_p.h +++ b/src/gui/painting/qpainter_p.h @@ -235,7 +235,7 @@ public: void drawTextItem(const QPointF &p, const QTextItem &_ti, QTextEngine *textEngine); #if !defined(QT_NO_RAWFONT) - void drawGlyphs(const quint32 *glyphArray, QFixedPoint *positionArray, int glyphCount, + void drawGlyphs(const QPointF &decorationPosition, const quint32 *glyphArray, QFixedPoint *positionArray, int glyphCount, QFontEngine *fontEngine, bool overline = false, bool underline = false, bool strikeOut = false); #endif diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index c092a7153f..e8ac494e04 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -40,43 +40,8 @@ #include "qplatformbackingstore.h" #include <qwindow.h> #include <qpixmap.h> -#include <private/qwindow_p.h> - -#include <qopengl.h> -#include <qopenglcontext.h> -#include <QtGui/QMatrix4x4> -#include <QtGui/QOpenGLShaderProgram> -#include <QtGui/QOpenGLContext> -#include <QtGui/QOpenGLFunctions> -#ifndef QT_NO_OPENGL -#include <QtGui/qopengltextureblitter.h> -#include <QtGui/qoffscreensurface.h> -#endif -#include <qpa/qplatformgraphicsbuffer.h> -#include <qpa/qplatformgraphicsbufferhelper.h> -#ifndef GL_TEXTURE_BASE_LEVEL -#define GL_TEXTURE_BASE_LEVEL 0x813C -#endif -#ifndef GL_TEXTURE_MAX_LEVEL -#define GL_TEXTURE_MAX_LEVEL 0x813D -#endif -#ifndef GL_UNPACK_ROW_LENGTH -#define GL_UNPACK_ROW_LENGTH 0x0CF2 -#endif -#ifndef GL_RGB10_A2 -#define GL_RGB10_A2 0x8059 -#endif -#ifndef GL_UNSIGNED_INT_2_10_10_10_REV -#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 -#endif - -#ifndef GL_FRAMEBUFFER_SRGB -#define GL_FRAMEBUFFER_SRGB 0x8DB9 -#endif -#ifndef GL_FRAMEBUFFER_SRGB_CAPABLE -#define GL_FRAMEBUFFER_SRGB_CAPABLE 0x8DBA -#endif +#include <QtCore/private/qobject_p.h> QT_BEGIN_NAMESPACE @@ -88,38 +53,19 @@ public: QPlatformBackingStorePrivate(QWindow *w) : window(w) , backingStore(nullptr) -#ifndef QT_NO_OPENGL - , textureId(0) - , blitter(nullptr) -#endif { } ~QPlatformBackingStorePrivate() { #ifndef QT_NO_OPENGL - if (context) { - QOffscreenSurface offscreenSurface; - offscreenSurface.setFormat(context->format()); - offscreenSurface.create(); - context->makeCurrent(&offscreenSurface); - if (textureId) - context->functions()->glDeleteTextures(1, &textureId); - if (blitter) - blitter->destroy(); - } - delete blitter; + delete openGLSupport; #endif } QWindow *window; QBackingStore *backingStore; #ifndef QT_NO_OPENGL - QScopedPointer<QOpenGLContext> context; - mutable GLuint textureId; - mutable QSize textureSize; - mutable bool needsSwizzle; - mutable bool premultiplied; - QOpenGLTextureBlitter *blitter; + QPlatformBackingStoreOpenGLSupportBase *openGLSupport = nullptr; #endif }; @@ -240,83 +186,20 @@ void QPlatformTextureList::clear() */ #ifndef QT_NO_OPENGL - -static inline QRect deviceRect(const QRect &rect, QWindow *window) -{ - QRect deviceRect(rect.topLeft() * window->devicePixelRatio(), - rect.size() * window->devicePixelRatio()); - return deviceRect; -} - -static inline QPoint deviceOffset(const QPoint &pt, QWindow *window) -{ - return pt * window->devicePixelRatio(); -} - -static QRegion deviceRegion(const QRegion ®ion, QWindow *window, const QPoint &offset) -{ - if (offset.isNull() && window->devicePixelRatio() <= 1) - return region; - - QVector<QRect> rects; - rects.reserve(region.rectCount()); - for (const QRect &rect : region) - rects.append(deviceRect(rect.translated(offset), window)); - - QRegion deviceRegion; - deviceRegion.setRects(rects.constData(), rects.count()); - return deviceRegion; -} - -static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight) -{ - return QRect(topLeftRect.x(), windowHeight - topLeftRect.bottomRight().y() - 1, - topLeftRect.width(), topLeftRect.height()); -} - -static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, QWindow *window, const QRect &deviceWindowRect, - QOpenGLTextureBlitter *blitter, const QPoint &offset, bool canUseSrgb) -{ - const QRect clipRect = textures->clipRect(idx); - if (clipRect.isEmpty()) - return; - - QRect rectInWindow = textures->geometry(idx); - // relative to the TLW, not necessarily our window (if the flush is for a native child widget), have to adjust - rectInWindow.translate(-offset); - - const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft()); - const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height()); - - const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(deviceRect(clippedRectInWindow, window), - deviceWindowRect); - - const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(deviceRect(srcRect, window), - deviceRect(rectInWindow, window).size(), - QOpenGLTextureBlitter::OriginBottomLeft); - - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - const bool srgb = textures->flags(idx).testFlag(QPlatformTextureList::TextureIsSrgb); - if (srgb && canUseSrgb) - funcs->glEnable(GL_FRAMEBUFFER_SRGB); - - blitter->blit(textures->textureId(idx), target, source); - - if (srgb && canUseSrgb) - funcs->glDisable(GL_FRAMEBUFFER_SRGB); -} - /*! Flushes the given \a region from the specified \a window onto the screen, and composes it with the specified \a textures. - The default implementation retrieves the contents using toTexture() + If OpenGLSupport has been enabled using \c setOpenGLSupport, + the default implementation retrieves the contents using toTexture() and composes using OpenGL. May be reimplemented in subclasses if there is a more efficient native way to do it. \note \a region is relative to the window which may not be top-level in case \a window corresponds to a native child widget. \a offset is the position of the native child relative to the top-level window. + + \sa setOpenGLSupport() */ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, @@ -324,162 +207,13 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i QPlatformTextureList *textures, bool translucentBackground) { - if (!qt_window_private(window)->receivedExpose) - return; - - if (!d_ptr->context) { - d_ptr->context.reset(new QOpenGLContext); - d_ptr->context->setFormat(d_ptr->window->requestedFormat()); - d_ptr->context->setScreen(d_ptr->window->screen()); - d_ptr->context->setShareContext(qt_window_private(d_ptr->window)->shareContext()); - if (!d_ptr->context->create()) { - qCWarning(lcQpaBackingStore, "composeAndFlush: QOpenGLContext creation failed"); - return; - } - } - - bool current = d_ptr->context->makeCurrent(window); - - if (!current && !d_ptr->context->isValid()) { - delete d_ptr->blitter; - d_ptr->blitter = nullptr; - d_ptr->textureId = 0; - current = d_ptr->context->create() && d_ptr->context->makeCurrent(window); - } - - if (!current) { - qCWarning(lcQpaBackingStore, "composeAndFlush: makeCurrent() failed"); - return; - } - - qCDebug(lcQpaBackingStore) << "Composing and flushing" << region << "of" << window - << "at offset" << offset << "with" << textures->count() << "texture(s) in" << textures; - - QWindowPrivate::get(window)->lastComposeTime.start(); - - QOpenGLFunctions *funcs = d_ptr->context->functions(); - funcs->glViewport(0, 0, qRound(window->width() * window->devicePixelRatio()), qRound(window->height() * window->devicePixelRatio())); - funcs->glClearColor(0, 0, 0, translucentBackground ? 0 : 1); - funcs->glClear(GL_COLOR_BUFFER_BIT); - - if (!d_ptr->blitter) { - d_ptr->blitter = new QOpenGLTextureBlitter; - d_ptr->blitter->create(); - } - - d_ptr->blitter->bind(); - - const QRect deviceWindowRect = deviceRect(QRect(QPoint(), window->size()), window); - const QPoint deviceWindowOffset = deviceOffset(offset, window); - - bool canUseSrgb = false; - // If there are any sRGB textures in the list, check if the destination - // framebuffer is sRGB capable. - for (int i = 0; i < textures->count(); ++i) { - if (textures->flags(i).testFlag(QPlatformTextureList::TextureIsSrgb)) { - GLint cap = 0; - funcs->glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE, &cap); - if (cap) - canUseSrgb = true; - break; - } - } - - // Textures for renderToTexture widgets. - for (int i = 0; i < textures->count(); ++i) { - if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) - blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset, canUseSrgb); - } - - // Backingstore texture with the normal widgets. - GLuint textureId = 0; - QOpenGLTextureBlitter::Origin origin = QOpenGLTextureBlitter::OriginTopLeft; - if (QPlatformGraphicsBuffer *graphicsBuffer = this->graphicsBuffer()) { - if (graphicsBuffer->size() != d_ptr->textureSize) { - if (d_ptr->textureId) - funcs->glDeleteTextures(1, &d_ptr->textureId); - funcs->glGenTextures(1, &d_ptr->textureId); - funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId); - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - } - funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - if (QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &d_ptr->needsSwizzle, &d_ptr->premultiplied)) { - d_ptr->textureSize = graphicsBuffer->size(); - } else { - d_ptr->textureSize = QSize(0,0); - } - - graphicsBuffer->unlock(); - } else if (!region.isEmpty()){ - funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId); - QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &d_ptr->needsSwizzle, &d_ptr->premultiplied); - graphicsBuffer->unlock(); - } - - if (graphicsBuffer->origin() == QPlatformGraphicsBuffer::OriginBottomLeft) - origin = QOpenGLTextureBlitter::OriginBottomLeft; - textureId = d_ptr->textureId; - } else { - TextureFlags flags; - textureId = toTexture(deviceRegion(region, window, offset), &d_ptr->textureSize, &flags); - d_ptr->needsSwizzle = (flags & TextureSwizzle) != 0; - d_ptr->premultiplied = (flags & TexturePremultiplied) != 0; - if (flags & TextureFlip) - origin = QOpenGLTextureBlitter::OriginBottomLeft; - } - - funcs->glEnable(GL_BLEND); - if (d_ptr->premultiplied) - funcs->glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + if (auto *c = d_ptr->openGLSupport) + c->composeAndFlush(window, region, offset, textures, translucentBackground); else - funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); - - if (textureId) { - if (d_ptr->needsSwizzle) - d_ptr->blitter->setRedBlueSwizzle(true); - // The backingstore is for the entire tlw. - // In case of native children offset tells the position relative to the tlw. - const QRect srcRect = toBottomLeftRect(deviceWindowRect.translated(deviceWindowOffset), d_ptr->textureSize.height()); - const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect, - d_ptr->textureSize, - origin); - d_ptr->blitter->blit(textureId, QMatrix4x4(), source); - if (d_ptr->needsSwizzle) - d_ptr->blitter->setRedBlueSwizzle(false); - } - - // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set. - bool blendIsPremultiplied = d_ptr->premultiplied; - for (int i = 0; i < textures->count(); ++i) { - const QPlatformTextureList::Flags flags = textures->flags(i); - if (flags.testFlag(QPlatformTextureList::NeedsPremultipliedAlphaBlending)) { - if (!blendIsPremultiplied) { - funcs->glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); - blendIsPremultiplied = true; - } - } else { - if (blendIsPremultiplied) { - funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); - blendIsPremultiplied = false; - } - } - if (flags.testFlag(QPlatformTextureList::StacksOnTop)) - blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset, canUseSrgb); - } - - funcs->glDisable(GL_BLEND); - d_ptr->blitter->release(); - - d_ptr->context->swapBuffers(window); + qWarning() << Q_FUNC_INFO << "no opengl support set"; } #endif + /*! Implemented in subclasses to return the content of the backingstore as a QImage. @@ -504,7 +238,8 @@ QImage QPlatformBackingStore::toImage() const The ownership of the texture is not transferred. The caller must not store the return value between calls, but instead call this function before each use. - The default implementation returns a cached texture if \a dirtyRegion is empty and + If OpenGLSupport has been enabled using \c setOpenGLSupport, + the default implementation returns a cached texture if \a dirtyRegion is empty and \a textureSize matches the backingstore size, otherwise it retrieves the content using toImage() and performs a texture upload. This works only if the value of \a textureSize is preserved between the calls to this function. @@ -520,141 +255,17 @@ QImage QPlatformBackingStore::toImage() const flags will be set to include \c TextureFlip. \note \a dirtyRegion is relative to the backingstore so no adjustment is needed. + + \sa setOpenGLSupport() */ GLuint QPlatformBackingStore::toTexture(const QRegion &dirtyRegion, QSize *textureSize, TextureFlags *flags) const { - Q_ASSERT(textureSize); - Q_ASSERT(flags); - - QImage image = toImage(); - QSize imageSize = image.size(); - - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - GLenum internalFormat = GL_RGBA; - GLuint pixelType = GL_UNSIGNED_BYTE; - - bool needsConversion = false; - *flags = { }; - switch (image.format()) { - case QImage::Format_ARGB32_Premultiplied: - *flags |= TexturePremultiplied; - Q_FALLTHROUGH(); - case QImage::Format_RGB32: - case QImage::Format_ARGB32: - *flags |= TextureSwizzle; - break; - case QImage::Format_RGBA8888_Premultiplied: - *flags |= TexturePremultiplied; - Q_FALLTHROUGH(); - case QImage::Format_RGBX8888: - case QImage::Format_RGBA8888: - break; - case QImage::Format_BGR30: - case QImage::Format_A2BGR30_Premultiplied: - if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { - pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; - internalFormat = GL_RGB10_A2; - *flags |= TexturePremultiplied; - } else { - needsConversion = true; - } - break; - case QImage::Format_RGB30: - case QImage::Format_A2RGB30_Premultiplied: - if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { - pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; - internalFormat = GL_RGB10_A2; - *flags |= TextureSwizzle | TexturePremultiplied; - } else { - needsConversion = true; - } - break; - default: - needsConversion = true; - break; - } - if (imageSize.isEmpty()) { - *textureSize = imageSize; + if (auto *c = d_ptr->openGLSupport) + return c->toTexture(dirtyRegion, textureSize, flags); + else { + qWarning() << Q_FUNC_INFO << "no opengl support set"; return 0; } - - // Must rely on the input only, not d_ptr. - // With the default composeAndFlush() textureSize is &d_ptr->textureSize. - bool resized = *textureSize != imageSize; - if (dirtyRegion.isEmpty() && !resized) - return d_ptr->textureId; - - *textureSize = imageSize; - - if (needsConversion) - image = image.convertToFormat(QImage::Format_RGBA8888); - - // The image provided by the backingstore may have a stride larger than width * 4, for - // instance on platforms that manually implement client-side decorations. - static const int bytesPerPixel = 4; - const int strideInPixels = image.bytesPerLine() / bytesPerPixel; - const bool hasUnpackRowLength = !ctx->isOpenGLES() || ctx->format().majorVersion() >= 3; - - QOpenGLFunctions *funcs = ctx->functions(); - - if (hasUnpackRowLength) { - funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, strideInPixels); - } else if (strideInPixels != image.width()) { - // No UNPACK_ROW_LENGTH on ES 2.0 and yet we would need it. This case is typically - // hit with QtWayland which is rarely used in combination with a ES2.0-only GL - // implementation. Therefore, accept the performance hit and do a copy. - image = image.copy(); - } - - if (resized) { - if (d_ptr->textureId) - funcs->glDeleteTextures(1, &d_ptr->textureId); - funcs->glGenTextures(1, &d_ptr->textureId); - funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId); - if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - } - funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, imageSize.width(), imageSize.height(), 0, GL_RGBA, pixelType, - const_cast<uchar*>(image.constBits())); - } else { - funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId); - QRect imageRect = image.rect(); - QRect rect = dirtyRegion.boundingRect() & imageRect; - - if (hasUnpackRowLength) { - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType, - image.constScanLine(rect.y()) + rect.x() * bytesPerPixel); - } else { - // if the rect is wide enough it's cheaper to just - // extend it instead of doing an image copy - if (rect.width() >= imageRect.width() / 2) { - rect.setX(0); - rect.setWidth(imageRect.width()); - } - - // if the sub-rect is full-width we can pass the image data directly to - // OpenGL instead of copying, since there's no gap between scanlines - - if (rect.width() == imageRect.width()) { - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType, - image.constScanLine(rect.y())); - } else { - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType, - image.copy(rect).constBits()); - } - } - } - - if (hasUnpackRowLength) - funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - return d_ptr->textureId; } #endif // QT_NO_OPENGL @@ -706,6 +317,18 @@ QBackingStore *QPlatformBackingStore::backingStore() const return d_ptr->backingStore; } +#ifndef QT_NO_OPENGL +/*! + Injects an OpenGL implementation helper. Platform integrations need to + call this if they intend to use the default OpenGL implementations of + composeAndFlush or toTexture. +*/ +void QPlatformBackingStore::setOpenGLSupport(QPlatformBackingStoreOpenGLSupportBase *openGLSupport) +{ + d_ptr->openGLSupport = openGLSupport; +} +#endif // QT_NO_OPENGL + /*! This function is called before painting onto the surface begins, with the \a region in which the painting will occur. diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h index 7aa054f1e2..2a3d7d20b5 100644 --- a/src/gui/painting/qplatformbackingstore.h +++ b/src/gui/painting/qplatformbackingstore.h @@ -67,11 +67,10 @@ class QRect; class QPoint; class QImage; class QPlatformBackingStorePrivate; -class QPlatformWindow; class QPlatformTextureList; class QPlatformTextureListPrivate; -class QOpenGLContext; class QPlatformGraphicsBuffer; +class QPlatformBackingStoreOpenGLSupportBase; #ifndef QT_NO_OPENGL class Q_GUI_EXPORT QPlatformTextureList : public QObject @@ -118,6 +117,8 @@ public: QWindow *window() const; QBackingStore *backingStore() const; + void setOpenGLSupport(QPlatformBackingStoreOpenGLSupportBase *openGLSupport); + virtual QPaintDevice *paintDevice() = 0; virtual void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) = 0; @@ -154,6 +155,17 @@ private: }; #ifndef QT_NO_OPENGL +class Q_GUI_EXPORT QPlatformBackingStoreOpenGLSupportBase // pure interface +{ +public: + virtual void composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, + QPlatformTextureList *textures, bool translucentBackground) = 0; + virtual GLuint toTexture(const QRegion &dirtyRegion, QSize *textureSize, QPlatformBackingStore::TextureFlags *flags) const = 0; + virtual ~QPlatformBackingStoreOpenGLSupportBase() {} +}; +#endif // QT_NO_OPENGL + +#ifndef QT_NO_OPENGL Q_DECLARE_OPERATORS_FOR_FLAGS(QPlatformBackingStore::TextureFlags) #endif diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index ba9590ec07..2a5d7edbee 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -24,6 +24,7 @@ HEADERS += \ qopengltexture.h \ qopengltexture_p.h \ qopengltexturehelper_p.h \ + qopengltextureblitter.h \ qopengltexturecache_p.h \ qopengltextureglyphcache_p.h \ qopengltextureuploader_p.h \ @@ -40,6 +41,7 @@ SOURCES += \ qopenglpixeltransferoptions.cpp \ qopengltexture.cpp \ qopengltexturehelper.cpp \ + qopengltextureblitter.cpp \ qopengltexturecache.cpp \ qopengltextureglyphcache.cpp \ qopengltextureuploader.cpp \ diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/opengl/qopengltextureblitter.cpp index b709f2f639..ba2eaf7754 100644 --- a/src/gui/opengl/qopengltextureblitter.cpp +++ b/src/opengl/qopengltextureblitter.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtGui module of the Qt Toolkit. +** This file is part of the QtOpenGL module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE \brief The QOpenGLTextureBlitter class provides a convenient way to draw textured quads via OpenGL. \since 5.8 \ingroup painting-3D - \inmodule QtGui + \inmodule QtOpenGL Drawing textured quads, in order to get the contents of a texture onto the screen, is a common operation when developing 2D user diff --git a/src/gui/opengl/qopengltextureblitter.h b/src/opengl/qopengltextureblitter.h index 2f7c6b1a0a..1818576085 100644 --- a/src/gui/opengl/qopengltextureblitter.h +++ b/src/opengl/qopengltextureblitter.h @@ -3,7 +3,7 @@ ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtGui module of the Qt Toolkit. +** This file is part of the QtOpenGL module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -40,9 +40,7 @@ #ifndef QOPENGLTEXTUREBLITTER_H #define QOPENGLTEXTUREBLITTER_H -#include <QtGui/qtguiglobal.h> - -#ifndef QT_NO_OPENGL +#include <QtOpenGL/qtopenglglobal.h> #include <QtGui/qopengl.h> #include <QtGui/QMatrix3x3> @@ -52,7 +50,7 @@ QT_BEGIN_NAMESPACE class QOpenGLTextureBlitterPrivate; -class Q_GUI_EXPORT QOpenGLTextureBlitter +class Q_OPENGL_EXPORT QOpenGLTextureBlitter { public: QOpenGLTextureBlitter(); @@ -89,6 +87,4 @@ private: QT_END_NAMESPACE -#endif - #endif //QOPENGLTEXTUREBLITTER_H diff --git a/src/opengl/qopenglwindow.cpp b/src/opengl/qopenglwindow.cpp index 9328d9b46a..5da20dc559 100644 --- a/src/opengl/qopenglwindow.cpp +++ b/src/opengl/qopenglwindow.cpp @@ -40,12 +40,12 @@ #include "qopenglwindow.h" #include <QtGui/QOpenGLFramebufferObject> #include <QtGui/QOpenGLFunctions> -#include <QtGui/QOpenGLTextureBlitter> #include <QtGui/private/qpaintdevicewindow_p.h> #include <QtGui/private/qopenglextensions_p.h> #include <QtGui/private/qopenglcontext_p.h> #include <QtGui/QMatrix4x4> #include <QtGui/QOffscreenSurface> +#include <QtOpenGL/QOpenGLTextureBlitter> #include <QtOpenGL/QOpenGLPaintDevice> QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/fontdatabases/windows/qwindowsdirectwritefontdatabase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsdirectwritefontdatabase.cpp new file mode 100644 index 0000000000..5a1ff935b8 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsdirectwritefontdatabase.cpp @@ -0,0 +1,465 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsdirectwritefontdatabase_p.h" +#include "qwindowsfontenginedirectwrite_p.h" + +#include <QtCore/qstringbuilder.h> +#include <QtCore/qvarlengtharray.h> + +#include <dwrite_3.h> +#include <d2d1.h> + +QT_BEGIN_NAMESPACE + +#ifdef QT_USE_DIRECTWRITE3 + +QWindowsDirectWriteFontDatabase::QWindowsDirectWriteFontDatabase() +{ + qCDebug(lcQpaFonts) << "Creating DirectWrite database"; +} + +QWindowsDirectWriteFontDatabase::~QWindowsDirectWriteFontDatabase() +{ + for (auto it = m_populatedFonts.begin(); it != m_populatedFonts.end(); ++it) + it.value()->Release(); +} + +QString QWindowsDirectWriteFontDatabase::localeString(IDWriteLocalizedStrings *names, + wchar_t localeName[]) +{ + uint index; + BOOL exists; + if (SUCCEEDED(names->FindLocaleName(localeName, &index, &exists)) && exists) { + uint length; + if (SUCCEEDED(names->GetStringLength(index, &length)) && length > 0) { + QVarLengthArray<wchar_t> buffer(int(length) + 1); + if (SUCCEEDED(names->GetString(index, buffer.data(), length + 1))) + return QString::fromWCharArray(buffer.data()); + } + } + + return QString(); +} + +static QFont::Stretch fromDirectWriteStretch(DWRITE_FONT_STRETCH stretch) +{ + switch (stretch) { + case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: return QFont::UltraCondensed; + case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: return QFont::ExtraCondensed; + case DWRITE_FONT_STRETCH_CONDENSED: return QFont::Condensed; + case DWRITE_FONT_STRETCH_SEMI_CONDENSED: return QFont::SemiCondensed; + case DWRITE_FONT_STRETCH_NORMAL: return QFont::Unstretched; + case DWRITE_FONT_STRETCH_SEMI_EXPANDED: return QFont::SemiExpanded; + case DWRITE_FONT_STRETCH_EXPANDED: return QFont::Expanded; + case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: return QFont::ExtraExpanded; + case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: return QFont::UltraExpanded; + default: return QFont::AnyStretch; + } +} + +static QFont::Weight fromDirectWriteWeight(DWRITE_FONT_WEIGHT weight) +{ + return QPlatformFontDatabase::weightFromInteger(int(weight)); +} + +static DWRITE_FONT_STYLE toDirectWriteStyle(QFont::Style style) +{ + switch (style) { + case QFont::StyleNormal: return DWRITE_FONT_STYLE_NORMAL; + case QFont::StyleOblique: return DWRITE_FONT_STYLE_OBLIQUE; + case QFont::StyleItalic: return DWRITE_FONT_STYLE_ITALIC; + default: return DWRITE_FONT_STYLE_NORMAL; + } +} + +static QFont::Style fromDirectWriteStyle(DWRITE_FONT_STYLE style) +{ + switch (style) { + case DWRITE_FONT_STYLE_NORMAL: return QFont::StyleNormal; + case DWRITE_FONT_STYLE_OBLIQUE: return QFont::StyleOblique; + case DWRITE_FONT_STYLE_ITALIC: return QFont::StyleItalic; + default: return QFont::StyleNormal; + } +} + +void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName) +{ + auto it = m_populatedFonts.find(familyName); + IDWriteFontFamily *fontFamily = it != m_populatedFonts.end() ? it.value() : nullptr; + if (fontFamily == nullptr) { + qCWarning(lcQpaFonts) << "Cannot find" << familyName << "in list of fonts"; + return; + } + + qCDebug(lcQpaFonts) << "Populate family:" << familyName; + + wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH]; + bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0; + wchar_t englishLocale[] = L"en-us"; + + static const int SMOOTH_SCALABLE = 0xffff; + const QString foundryName; // No such concept. + const bool scalable = true; + const bool antialias = false; + const int size = SMOOTH_SCALABLE; + + IDWriteFontList *matchingFonts; + if (SUCCEEDED(fontFamily->GetMatchingFonts(DWRITE_FONT_WEIGHT_REGULAR, + DWRITE_FONT_STRETCH_NORMAL, + DWRITE_FONT_STYLE_NORMAL, + &matchingFonts))) { + for (uint j = 0; j < matchingFonts->GetFontCount(); ++j) { + IDWriteFont *font; + if (SUCCEEDED(matchingFonts->GetFont(j, &font))) { + IDWriteFont1 *font1 = nullptr; + if (!SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1), + reinterpret_cast<void **>(&font1)))) { + qCWarning(lcQpaFonts) << "COM object does not support IDWriteFont1"; + continue; + } + + QString defaultLocaleFamilyName; + QString englishLocaleFamilyName; + + IDWriteFontFamily *fontFamily2; + if (SUCCEEDED(font1->GetFontFamily(&fontFamily2))) { + IDWriteLocalizedStrings *names; + if (SUCCEEDED(fontFamily2->GetFamilyNames(&names))) { + defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); + englishLocaleFamilyName = localeString(names, englishLocale); + + names->Release(); + } + + fontFamily2->Release(); + } + + if (defaultLocaleFamilyName.isEmpty() && englishLocaleFamilyName.isEmpty()) + englishLocaleFamilyName = familyName; + + QVarLengthArray<DWRITE_UNICODE_RANGE, 64> ranges(QFontDatabase::WritingSystemsCount); + + uint count = 0; + if (SUCCEEDED(font1->GetUnicodeRanges(QFontDatabase::WritingSystemsCount, ranges.data(), &count))) { + // ### + } + QSupportedWritingSystems writingSystems; + writingSystems.setSupported(QFontDatabase::Any); + writingSystems.setSupported(QFontDatabase::Latin); + + { + IDWriteLocalizedStrings *names; + if (SUCCEEDED(font1->GetFaceNames(&names))) { + QString defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); + QString englishLocaleStyleName = localeString(names, englishLocale); + + QFont::Stretch stretch = fromDirectWriteStretch(font1->GetStretch()); + QFont::Style style = fromDirectWriteStyle(font1->GetStyle()); + QFont::Weight weight = fromDirectWriteWeight(font1->GetWeight()); + bool fixed = font1->IsMonospacedFont(); + + qCDebug(lcQpaFonts) << "Family" << familyName << "has english variant" << englishLocaleStyleName << ", in default locale:" << defaultLocaleStyleName << stretch << style << weight << fixed; + + IDWriteFontFace *face = nullptr; + if (SUCCEEDED(font->CreateFontFace(&face))) { + if (!englishLocaleStyleName.isEmpty() || defaultLocaleStyleName.isEmpty()) { + QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face); + face->AddRef(); + } + + if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) { + QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face); + face->AddRef(); + } + + face->Release(); + } + + names->Release(); + } + } + + font1->Release(); + font->Release(); + } + } + + matchingFonts->Release(); + } +} + +QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QFontDef &fontDef, void *handle) +{ + IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle); + Q_ASSERT(face != nullptr); + + QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(face, fontDef.pixelSize, data()); + fontEngine->initFontInfo(fontDef, defaultVerticalDPI()); + + return fontEngine; +} + +QStringList QWindowsDirectWriteFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const +{ + Q_UNUSED(styleHint); + Q_UNUSED(script); + + wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH]; + bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0; + wchar_t englishLocale[] = L"en-us"; + + QStringList ret; + + auto it = m_populatedFonts.find(family); + IDWriteFontFamily *fontFamily = it != m_populatedFonts.end() ? it.value() : nullptr; + if (fontFamily != nullptr) { + IDWriteFontList *matchingFonts = nullptr; + if (SUCCEEDED(fontFamily->GetMatchingFonts(DWRITE_FONT_WEIGHT_REGULAR, + DWRITE_FONT_STRETCH_NORMAL, + toDirectWriteStyle(style), + &matchingFonts))) { + for (uint j = 0; j < matchingFonts->GetFontCount(); ++j) { + IDWriteFont *font = nullptr; + if (SUCCEEDED(matchingFonts->GetFont(j, &font))) { + IDWriteFontFamily *fontFamily2; + if (SUCCEEDED(font->GetFontFamily(&fontFamily2))) { + IDWriteLocalizedStrings *names; + if (SUCCEEDED(fontFamily2->GetFamilyNames(&names))) { + QString name = localeString(names, englishLocale); + if (name.isEmpty() && hasDefaultLocale) + name = localeString(names, defaultLocale); + + if (!name.isEmpty() && m_populatedFonts.contains(name)) + ret.append(name); + + names->Release(); + } + + fontFamily2->Release(); + } + + font->Release(); + } + } + + matchingFonts->Release(); + } + } + + qDebug(lcQpaFonts) << "fallbacks for" << family << "is" << ret; + + return ret; +} + +QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) +{ + qCDebug(lcQpaFonts) << "Adding application font" << fileName; + + QByteArray loadedData = fontData; + if (loadedData.isEmpty()) { + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) { + qCWarning(lcQpaFonts) << "Cannot open" << fileName << "for reading."; + return QStringList(); + } + loadedData = file.readAll(); + } + + IDWriteFontFace *face = createDirectWriteFace(loadedData); + if (face == nullptr) { + qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported."; + return QStringList(); + } + + wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH]; + bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0; + wchar_t englishLocale[] = L"en-us"; + + static const int SMOOTH_SCALABLE = 0xffff; + const QString foundryName; // No such concept. + const bool scalable = true; + const bool antialias = false; + const int size = SMOOTH_SCALABLE; + + QSupportedWritingSystems writingSystems; + writingSystems.setSupported(QFontDatabase::Any); + writingSystems.setSupported(QFontDatabase::Latin); + + QStringList ret; + IDWriteFontFace3 *face3 = nullptr; + if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3), + reinterpret_cast<void **>(&face3)))) { + QString defaultLocaleFamilyName; + QString englishLocaleFamilyName; + + IDWriteLocalizedStrings *names; + if (SUCCEEDED(face3->GetFamilyNames(&names))) { + defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); + englishLocaleFamilyName = localeString(names, englishLocale); + + names->Release(); + } + + QString defaultLocaleStyleName; + QString englishLocaleStyleName; + if (SUCCEEDED(face3->GetFaceNames(&names))) { + defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString(); + englishLocaleStyleName = localeString(names, englishLocale); + + names->Release(); + } + + QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch()); + QFont::Style style = fromDirectWriteStyle(face3->GetStyle()); + QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight()); + bool fixed = face3->IsMonospacedFont(); + + qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName + << ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName + << ", stretch:" << stretch + << ", style:" << style + << ", weight:" << weight + << ", fixed:" << fixed; + + if (!englishLocaleFamilyName.isEmpty()) { + ret.append(englishLocaleFamilyName); + QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face); + face->AddRef(); + } + + if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) { + ret.append(defaultLocaleFamilyName); + QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face); + face->AddRef(); + } + + face3->Release(); + } else { + qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face."; + } + + face->Release(); + + return ret; +} + +void QWindowsDirectWriteFontDatabase::releaseHandle(void *handle) +{ + IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle); + face->Release(); +} + +bool QWindowsDirectWriteFontDatabase::fontsAlwaysScalable() const +{ + return true; +} + +bool QWindowsDirectWriteFontDatabase::isPrivateFontFamily(const QString &family) const +{ + Q_UNUSED(family); + return false; +} + +void QWindowsDirectWriteFontDatabase::populateFontDatabase() +{ + wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH]; + bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0; + wchar_t englishLocale[] = L"en-us"; + + QString defaultFontName = defaultFont().family(); + QString systemDefaultFontName = systemDefaultFont().family(); + + IDWriteFontCollection *fontCollection; + if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) { + for (uint i = 0; i < fontCollection->GetFontFamilyCount(); ++i) { + IDWriteFontFamily *fontFamily; + if (SUCCEEDED(fontCollection->GetFontFamily(i, &fontFamily))) { + QString defaultLocaleName; + QString englishLocaleName; + + IDWriteLocalizedStrings *names; + if (SUCCEEDED(fontFamily->GetFamilyNames(&names))) { + if (hasDefaultLocale) + defaultLocaleName = localeString(names, defaultLocale); + + englishLocaleName = localeString(names, englishLocale); + } + + qCDebug(lcQpaFonts) << "Registering font, english name = " << englishLocaleName << ", name in current locale = " << defaultLocaleName; + if (!defaultLocaleName.isEmpty()) { + registerFontFamily(defaultLocaleName); + m_populatedFonts.insert(defaultLocaleName, fontFamily); + fontFamily->AddRef(); + + if (defaultLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) { + qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << defaultLocaleName; + + m_populatedFonts.insert(systemDefaultFontName, fontFamily); + fontFamily->AddRef(); + } + } + + if (!englishLocaleName.isEmpty() && englishLocaleName != defaultLocaleName) { + registerFontFamily(englishLocaleName); + m_populatedFonts.insert(englishLocaleName, fontFamily); + fontFamily->AddRef(); + + if (englishLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) { + qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << englishLocaleName; + + m_populatedFonts.insert(systemDefaultFontName, fontFamily); + fontFamily->AddRef(); + } + } + + fontFamily->Release(); + } + } + } +} + +QFont QWindowsDirectWriteFontDatabase::defaultFont() const +{ + return QFont(QStringLiteral("Segoe UI")); +} + +#endif // QT_USE_DIRECTWRITE3 + +QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/windows/qwindowsdirectwritefontdatabase_p.h b/src/platformsupport/fontdatabases/windows/qwindowsdirectwritefontdatabase_p.h new file mode 100644 index 0000000000..2110043df6 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsdirectwritefontdatabase_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSDIRECTWRITEFONTDATABASE_P_H +#define QWINDOWSDIRECTWRITEFONTDATABASE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qwindowsfontdatabasebase_p.h" + +#include <qpa/qplatformfontdatabase.h> +#include <QtCore/qloggingcategory.h> + +struct IDWriteFactory; +struct IDWriteFont; +struct IDWriteFontFamily; +struct IDWriteLocalizedStrings; + +QT_BEGIN_NAMESPACE + +#ifdef QT_USE_DIRECTWRITE3 + +class QWindowsDirectWriteFontDatabase : public QWindowsFontDatabaseBase +{ + Q_DISABLE_COPY_MOVE(QWindowsDirectWriteFontDatabase) +public: + QWindowsDirectWriteFontDatabase(); + ~QWindowsDirectWriteFontDatabase() override; + + void populateFontDatabase() override; + void populateFamily(const QString &familyName) override; + QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override; + QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override; + QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName) override; + void releaseHandle(void *handle) override; + QFont defaultFont() const override; + + bool fontsAlwaysScalable() const override; + bool isPrivateFontFamily(const QString &family) const override; + +private: + static QString localeString(IDWriteLocalizedStrings *names, wchar_t localeName[]); + + QHash<QString, IDWriteFontFamily *> m_populatedFonts; +}; + +#endif // QT_USE_DIRECTWRITE3 + +QT_END_NAMESPACE + +#endif // QWINDOWSDIRECTWRITEFONTDATABASE_P_H diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp index 36a94724c1..cf5763fdbe 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp @@ -51,7 +51,6 @@ #include <QtCore/QDebug> #include <QtCore/QFile> #include <QtCore/QtEndian> -#include <QtCore/QThreadStorage> #include <QtCore/private/qsystemlibrary_p.h> #include <QtCore/private/qwinregistry_p.h> @@ -64,6 +63,7 @@ # include <dwrite.h> # endif # include <d2d1.h> +# include "qwindowsdirectwritefontdatabase_p.h" #endif QT_BEGIN_NAMESPACE @@ -75,40 +75,6 @@ Q_LOGGING_CATEGORY(lcQpaFonts, "qt.qpa.fonts") typedef HRESULT (WINAPI *DWriteCreateFactoryType)(DWRITE_FACTORY_TYPE, const IID &, IUnknown **); -static inline DWriteCreateFactoryType resolveDWriteCreateFactory() -{ - QSystemLibrary library(QStringLiteral("dwrite")); - QFunctionPointer result = library.resolve("DWriteCreateFactory"); - if (Q_UNLIKELY(!result)) { - qWarning("Unable to load dwrite.dll"); - return nullptr; - } - return reinterpret_cast<DWriteCreateFactoryType>(result); -} - -static void createDirectWriteFactory(IDWriteFactory **factory) -{ - *factory = nullptr; - - static const DWriteCreateFactoryType dWriteCreateFactory = resolveDWriteCreateFactory(); - if (!dWriteCreateFactory) - return; - - IUnknown *result = NULL; -#if defined(QT_USE_DIRECTWRITE2) - dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result); -#endif - - if (result == NULL) { - if (FAILED(dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) { - qErrnoWarning("DWriteCreateFactory failed"); - return; - } - } - - *factory = static_cast<IDWriteFactory *>(result); -} - static inline bool useDirectWrite(QFont::HintingPreference hintingPreference, const QString &familyName = QString(), bool isColorFont = false) @@ -131,459 +97,6 @@ static inline bool useDirectWrite(QFont::HintingPreference hintingPreference, } #endif // !QT_NO_DIRECTWRITE -// Helper classes for creating font engines directly from font data -namespace { - -# pragma pack(1) - - // Common structure for all formats of the "name" table - struct NameTable - { - quint16 format; - quint16 count; - quint16 stringOffset; - }; - - struct NameRecord - { - quint16 platformID; - quint16 encodingID; - quint16 languageID; - quint16 nameID; - quint16 length; - quint16 offset; - }; - - struct OffsetSubTable - { - quint32 scalerType; - quint16 numTables; - quint16 searchRange; - quint16 entrySelector; - quint16 rangeShift; - }; - - struct TableDirectory - { - quint32 identifier; - quint32 checkSum; - quint32 offset; - quint32 length; - }; - - struct OS2Table - { - quint16 version; - qint16 avgCharWidth; - quint16 weightClass; - quint16 widthClass; - quint16 type; - qint16 subscriptXSize; - qint16 subscriptYSize; - qint16 subscriptXOffset; - qint16 subscriptYOffset; - qint16 superscriptXSize; - qint16 superscriptYSize; - qint16 superscriptXOffset; - qint16 superscriptYOffset; - qint16 strikeOutSize; - qint16 strikeOutPosition; - qint16 familyClass; - quint8 panose[10]; - quint32 unicodeRanges[4]; - quint8 vendorID[4]; - quint16 selection; - quint16 firstCharIndex; - quint16 lastCharIndex; - qint16 typoAscender; - qint16 typoDescender; - qint16 typoLineGap; - quint16 winAscent; - quint16 winDescent; - quint32 codepageRanges[2]; - qint16 height; - qint16 capHeight; - quint16 defaultChar; - quint16 breakChar; - quint16 maxContext; - }; - -# pragma pack() - - class EmbeddedFont - { - public: - EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) {} - - QString changeFamilyName(const QString &newFamilyName); - QByteArray data() const { return m_fontData; } - TableDirectory *tableDirectoryEntry(const QByteArray &tagName); - QString familyName(TableDirectory *nameTableDirectory = 0); - - private: - QByteArray m_fontData; - }; - - TableDirectory *EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName) - { - Q_ASSERT(tagName.size() == 4); - quint32 tagId = *(reinterpret_cast<const quint32 *>(tagName.constData())); - const size_t fontDataSize = m_fontData.size(); - if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable))) - return 0; - - OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data()); - TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1); - - const size_t tableCount = qFromBigEndian<quint16>(offsetSubTable->numTables); - if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable) + sizeof(TableDirectory) * tableCount)) - return 0; - - TableDirectory *tableDirectoryEnd = tableDirectory + tableCount; - for (TableDirectory *entry = tableDirectory; entry < tableDirectoryEnd; ++entry) { - if (entry->identifier == tagId) - return entry; - } - - return 0; - } - - QString EmbeddedFont::familyName(TableDirectory *nameTableDirectoryEntry) - { - QString name; - - if (nameTableDirectoryEntry == 0) - nameTableDirectoryEntry = tableDirectoryEntry("name"); - - if (nameTableDirectoryEntry != 0) { - quint32 offset = qFromBigEndian<quint32>(nameTableDirectoryEntry->offset); - if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameTable))) - return QString(); - - NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() + offset); - NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); - - quint16 nameTableCount = qFromBigEndian<quint16>(nameTable->count); - if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameRecord) * nameTableCount)) - return QString(); - - for (int i = 0; i < nameTableCount; ++i, ++nameRecord) { - if (qFromBigEndian<quint16>(nameRecord->nameID) == 1 - && qFromBigEndian<quint16>(nameRecord->platformID) == 3 // Windows - && qFromBigEndian<quint16>(nameRecord->languageID) == 0x0409) { // US English - quint16 stringOffset = qFromBigEndian<quint16>(nameTable->stringOffset); - quint16 nameOffset = qFromBigEndian<quint16>(nameRecord->offset); - quint16 nameLength = qFromBigEndian<quint16>(nameRecord->length); - - if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + stringOffset + nameOffset + nameLength)) - return QString(); - - const void *ptr = reinterpret_cast<const quint8 *>(nameTable) - + stringOffset - + nameOffset; - - const quint16 *s = reinterpret_cast<const quint16 *>(ptr); - const quint16 *e = s + nameLength / sizeof(quint16); - while (s != e) - name += QChar( qFromBigEndian<quint16>(*s++)); - break; - } - } - } - - return name; - } - - QString EmbeddedFont::changeFamilyName(const QString &newFamilyName) - { - TableDirectory *nameTableDirectoryEntry = tableDirectoryEntry("name"); - if (nameTableDirectoryEntry == 0) - return QString(); - - QString oldFamilyName = familyName(nameTableDirectoryEntry); - - // Reserve size for name table header, five required name records and string - const int requiredRecordCount = 5; - quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 }; - - int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount; - int newFamilyNameSize = newFamilyName.size() * int(sizeof(quint16)); - - const QString regularString = QString::fromLatin1("Regular"); - int regularStringSize = regularString.size() * int(sizeof(quint16)); - - // Align table size of table to 32 bits (pad with 0) - int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4; - - QByteArray newNameTable(fullSize, char(0)); - - { - NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data()); - nameTable->count = qbswap<quint16>(requiredRecordCount); - nameTable->stringOffset = qbswap<quint16>(sizeOfHeader); - - NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); - for (int i = 0; i < requiredRecordCount; ++i, nameRecord++) { - nameRecord->nameID = qbswap<quint16>(nameIds[i]); - nameRecord->encodingID = qbswap<quint16>(1); - nameRecord->languageID = qbswap<quint16>(0x0409); - nameRecord->platformID = qbswap<quint16>(3); - nameRecord->length = qbswap<quint16>(newFamilyNameSize); - - // Special case for sub-family - if (nameIds[i] == 4) { - nameRecord->offset = qbswap<quint16>(newFamilyNameSize); - nameRecord->length = qbswap<quint16>(regularStringSize); - } - } - - // nameRecord now points to string data - quint16 *stringStorage = reinterpret_cast<quint16 *>(nameRecord); - const quint16 *sourceString = newFamilyName.utf16(); - for (int i = 0; i < newFamilyName.size(); ++i) - stringStorage[i] = qbswap<quint16>(sourceString[i]); - stringStorage += newFamilyName.size(); - - sourceString = regularString.utf16(); - for (int i = 0; i < regularString.size(); ++i) - stringStorage[i] = qbswap<quint16>(sourceString[i]); - } - - quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data()); - quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize); - - quint32 checkSum = 0; - while (p < tableEnd) - checkSum += qFromBigEndian<quint32>(*(p++)); - - nameTableDirectoryEntry->checkSum = qbswap<quint32>(checkSum); - nameTableDirectoryEntry->offset = qbswap<quint32>(m_fontData.size()); - nameTableDirectoryEntry->length = qbswap<quint32>(fullSize); - - m_fontData.append(newNameTable); - - return oldFamilyName; - } - -#if !defined(QT_NO_DIRECTWRITE) - - class DirectWriteFontFileStream: public IDWriteFontFileStream - { - Q_DISABLE_COPY(DirectWriteFontFileStream) - public: - DirectWriteFontFileStream(const QByteArray &fontData) - : m_fontData(fontData) - , m_referenceCount(0) - { - } - virtual ~DirectWriteFontFileStream() - { - } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); - ULONG STDMETHODCALLTYPE AddRef(); - ULONG STDMETHODCALLTYPE Release(); - - HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset, - UINT64 fragmentSize, OUT void **fragmentContext); - void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext); - HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize); - HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime); - - private: - QByteArray m_fontData; - ULONG m_referenceCount; - }; - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object) - { - if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) { - *object = this; - AddRef(); - return S_OK; - } else { - *object = NULL; - return E_NOINTERFACE; - } - } - - ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef() - { - return InterlockedIncrement(&m_referenceCount); - } - - ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release() - { - ULONG newCount = InterlockedDecrement(&m_referenceCount); - if (newCount == 0) - delete this; - return newCount; - } - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment( - const void **fragmentStart, - UINT64 fileOffset, - UINT64 fragmentSize, - OUT void **fragmentContext) - { - *fragmentContext = NULL; - if (fileOffset + fragmentSize <= quint64(m_fontData.size())) { - *fragmentStart = m_fontData.data() + fileOffset; - return S_OK; - } else { - *fragmentStart = NULL; - return E_FAIL; - } - } - - void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *) - { - } - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize) - { - *fileSize = m_fontData.size(); - return S_OK; - } - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) - { - *lastWriteTime = 0; - return E_NOTIMPL; - } - - class DirectWriteFontFileLoader: public IDWriteFontFileLoader - { - public: - DirectWriteFontFileLoader() : m_referenceCount(0) {} - virtual ~DirectWriteFontFileLoader() - { - } - - inline void addKey(const void *key, const QByteArray &fontData) - { - Q_ASSERT(!m_fontDatas.contains(key)); - m_fontDatas.insert(key, fontData); - } - - inline void removeKey(const void *key) - { - m_fontDatas.remove(key); - } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); - ULONG STDMETHODCALLTYPE AddRef(); - ULONG STDMETHODCALLTYPE Release(); - - HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey, - UINT32 fontFileReferenceKeySize, - OUT IDWriteFontFileStream **fontFileStream); - - private: - ULONG m_referenceCount; - QHash<const void *, QByteArray> m_fontDatas; - }; - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid, - void **object) - { - if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { - *object = this; - AddRef(); - return S_OK; - } else { - *object = NULL; - return E_NOINTERFACE; - } - } - - ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef() - { - return InterlockedIncrement(&m_referenceCount); - } - - ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release() - { - ULONG newCount = InterlockedDecrement(&m_referenceCount); - if (newCount == 0) - delete this; - return newCount; - } - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey( - void const *fontFileReferenceKey, - UINT32 fontFileReferenceKeySize, - IDWriteFontFileStream **fontFileStream) - { - Q_UNUSED(fontFileReferenceKeySize); - - if (fontFileReferenceKeySize != sizeof(const void *)) { - qWarning("%s: Wrong key size", __FUNCTION__); - return E_FAIL; - } - - const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey); - *fontFileStream = NULL; - auto it = m_fontDatas.constFind(key); - if (it == m_fontDatas.constEnd()) - return E_FAIL; - - QByteArray fontData = it.value(); - DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData); - stream->AddRef(); - *fontFileStream = stream; - - return S_OK; - } - - class CustomFontFileLoader - { - public: - CustomFontFileLoader() : m_directWriteFontFileLoader(nullptr) - { - createDirectWriteFactory(&m_directWriteFactory); - - if (m_directWriteFactory) { - m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); - m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); - } - } - - ~CustomFontFileLoader() - { - if (m_directWriteFactory != 0 && m_directWriteFontFileLoader != 0) - m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); - - if (m_directWriteFactory != 0) - m_directWriteFactory->Release(); - } - - void addKey(const void *key, const QByteArray &fontData) - { - if (m_directWriteFontFileLoader != 0) - m_directWriteFontFileLoader->addKey(key, fontData); - } - - void removeKey(const void *key) - { - if (m_directWriteFontFileLoader != 0) - m_directWriteFontFileLoader->removeKey(key); - } - - IDWriteFontFileLoader *loader() const - { - return m_directWriteFontFileLoader; - } - - private: - IDWriteFactory *m_directWriteFactory; - DirectWriteFontFileLoader *m_directWriteFontFileLoader; - }; - -#endif - -} // Anonymous namespace - /*! \struct QWindowsFontEngineData \brief Static constant data shared by the font engines. @@ -620,18 +133,6 @@ unsigned QWindowsFontDatabase::fontOptions() return m_fontOptions; } -QWindowsFontEngineData::~QWindowsFontEngineData() -{ - if (hdc) - DeleteDC(hdc); -#if !defined(QT_NO_DIRECTWRITE) - if (directWriteGdiInterop) - directWriteGdiInterop->Release(); - if (directWriteFactory) - directWriteFactory->Release(); -#endif -} - qreal QWindowsFontDatabase::fontSmoothingGamma() { int winSmooth; @@ -645,26 +146,6 @@ qreal QWindowsFontDatabase::fontSmoothingGamma() return result; } -#if !defined(QT_NO_DIRECTWRITE) -static inline bool initDirectWrite(QWindowsFontEngineData *d) -{ - if (!d->directWriteFactory) { - createDirectWriteFactory(&d->directWriteFactory); - if (!d->directWriteFactory) - return false; - } - if (!d->directWriteGdiInterop) { - const HRESULT hr = d->directWriteFactory->GetGdiInterop(&d->directWriteGdiInterop); - if (FAILED(hr)) { - qErrnoWarning("%s: GetGdiInterop failed", __FUNCTION__); - return false; - } - } - return true; -} - -#endif // !defined(QT_NO_DIRECTWRITE) - /*! \class QWindowsFontDatabase \brief Font database for Windows @@ -1239,20 +720,6 @@ void QWindowsFontDatabase::populateFontDatabase() addDefaultEUDCFont(); } -typedef QSharedPointer<QWindowsFontEngineData> QWindowsFontEngineDataPtr; - -typedef QThreadStorage<QWindowsFontEngineDataPtr> FontEngineThreadLocalData; - -Q_GLOBAL_STATIC(FontEngineThreadLocalData, fontEngineThreadLocalData) - -QSharedPointer<QWindowsFontEngineData> sharedFontData() -{ - FontEngineThreadLocalData *data = fontEngineThreadLocalData(); - if (!data->hasLocalData()) - data->setLocalData(QSharedPointer<QWindowsFontEngineData>::create()); - return data->localData(); -} - QWindowsFontDatabase::QWindowsFontDatabase() { // Properties accessed by QWin32PrintEngine (Qt Print Support) @@ -1262,9 +729,9 @@ QWindowsFontDatabase::QWindowsFontDatabase() Q_UNUSED(logFontMetaTypeId) if (lcQpaFonts().isDebugEnabled()) { - const QWindowsFontEngineDataPtr data = sharedFontData(); + QSharedPointer<QWindowsFontEngineData> d = data(); qCDebug(lcQpaFonts) << __FUNCTION__ << "Clear type: " - << data->clearTypeEnabled << "gamma: " << data->fontSmoothingGamma; + << d->clearTypeEnabled << "gamma: " << d->fontSmoothingGamma; } } @@ -1278,7 +745,7 @@ QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, void *ha const QString faceName(static_cast<const QChar*>(handle)); QFontEngine *fe = QWindowsFontDatabase::createEngine(fontDef, faceName, defaultVerticalDPI(), - sharedFontData()); + data()); qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef << fe << handle; return fe; } @@ -1332,7 +799,7 @@ QT_WARNING_POP fontEngine = QWindowsFontDatabase::createEngine(request, QString(), defaultVerticalDPI(), - sharedFontData()); + data()); if (fontEngine) { if (request.family != fontEngine->fontDef.family) { @@ -1371,87 +838,17 @@ QT_WARNING_POP RemoveFontMemResourceEx(fontHandle); } } + + // Get style and weight info + if (fontEngine != nullptr) + font.updateFromOS2Table(fontEngine); } #if !defined(QT_NO_DIRECTWRITE) else { - CustomFontFileLoader fontFileLoader; - fontFileLoader.addKey(this, fontData); - - QSharedPointer<QWindowsFontEngineData> fontEngineData = sharedFontData(); - if (!initDirectWrite(fontEngineData.data())) - return 0; - - IDWriteFontFile *fontFile = 0; - void *key = this; - - HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key, - sizeof(void *), - fontFileLoader.loader(), - &fontFile); - if (FAILED(hres)) { - qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__); - return 0; - } - - BOOL isSupportedFontType; - DWRITE_FONT_FILE_TYPE fontFileType; - DWRITE_FONT_FACE_TYPE fontFaceType; - UINT32 numberOfFaces; - fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces); - if (!isSupportedFontType) { - fontFile->Release(); - return 0; - } - - IDWriteFontFace *directWriteFontFace = 0; - hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType, - 1, - &fontFile, - 0, - DWRITE_FONT_SIMULATIONS_NONE, - &directWriteFontFace); - if (FAILED(hres)) { - qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__); - fontFile->Release(); - return 0; - } - - fontFile->Release(); - - fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace, - pixelSize, - fontEngineData); - - // Get font family from font data - fontEngine->fontDef.family = font.familyName(); - fontEngine->fontDef.hintingPreference = hintingPreference; - - directWriteFontFace->Release(); + fontEngine = QWindowsFontDatabaseBase::fontEngine(fontData, pixelSize, hintingPreference); } #endif - // Get style and weight info - if (fontEngine != 0) { - TableDirectory *os2TableEntry = font.tableDirectoryEntry("OS/2"); - if (os2TableEntry != 0) { - const OS2Table *os2Table = - reinterpret_cast<const OS2Table *>(fontData.constData() - + qFromBigEndian<quint32>(os2TableEntry->offset)); - - bool italic = qFromBigEndian<quint16>(os2Table->selection) & (1 << 0); - bool oblique = qFromBigEndian<quint16>(os2Table->selection) & (1 << 9); - - if (italic) - fontEngine->fontDef.style = QFont::StyleItalic; - else if (oblique) - fontEngine->fontDef.style = QFont::StyleOblique; - else - fontEngine->fontDef.style = QFont::StyleNormal; - - fontEngine->fontDef.weight = QPlatformFontDatabase::weightFromInteger(qFromBigEndian<quint16>(os2Table->weightClass)); - } - } - qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fontEngine; return fontEngine; } @@ -1683,14 +1080,6 @@ void QWindowsFontDatabase::refUniqueFont(const QString &uniqueFont) m_uniqueFontData[uniqueFont].refCount.ref(); } -// ### fixme Qt 6 (QTBUG-58610): See comment at QWindowsFontDatabase::systemDefaultFont() -HFONT QWindowsFontDatabase::systemFont() -{ - static const auto stock_sysfont = - reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT)); - return stock_sysfont; -} - // Creation functions static const char *other_tryFonts[] = { @@ -1745,101 +1134,6 @@ static const char *kr_tryFonts[] = { static const char **tryFonts = 0; -LOGFONT QWindowsFontDatabase::fontDefToLOGFONT(const QFontDef &request, const QString &faceName) -{ - LOGFONT lf; - memset(&lf, 0, sizeof(LOGFONT)); - - lf.lfHeight = -qRound(request.pixelSize); - lf.lfWidth = 0; - lf.lfEscapement = 0; - lf.lfOrientation = 0; - if (request.weight == 50) - lf.lfWeight = FW_DONTCARE; - else - lf.lfWeight = (request.weight*900)/99; - lf.lfItalic = request.style != QFont::StyleNormal; - lf.lfCharSet = DEFAULT_CHARSET; - - int strat = OUT_DEFAULT_PRECIS; - if (request.styleStrategy & QFont::PreferBitmap) { - strat = OUT_RASTER_PRECIS; - } else if (request.styleStrategy & QFont::PreferDevice) { - strat = OUT_DEVICE_PRECIS; - } else if (request.styleStrategy & QFont::PreferOutline) { - strat = OUT_OUTLINE_PRECIS; - } else if (request.styleStrategy & QFont::ForceOutline) { - strat = OUT_TT_ONLY_PRECIS; - } - - lf.lfOutPrecision = strat; - - int qual = DEFAULT_QUALITY; - - if (request.styleStrategy & QFont::PreferMatch) - qual = DRAFT_QUALITY; - else if (request.styleStrategy & QFont::PreferQuality) - qual = PROOF_QUALITY; - - if (request.styleStrategy & QFont::PreferAntialias) { - qual = (request.styleStrategy & QFont::NoSubpixelAntialias) == 0 - ? CLEARTYPE_QUALITY : ANTIALIASED_QUALITY; - } else if (request.styleStrategy & QFont::NoAntialias) { - qual = NONANTIALIASED_QUALITY; - } else if ((request.styleStrategy & QFont::NoSubpixelAntialias) && sharedFontData()->clearTypeEnabled) { - qual = ANTIALIASED_QUALITY; - } - - lf.lfQuality = qual; - - lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; - - int hint = FF_DONTCARE; - switch (request.styleHint) { - case QFont::Helvetica: - hint = FF_SWISS; - break; - case QFont::Times: - hint = FF_ROMAN; - break; - case QFont::Courier: - hint = FF_MODERN; - break; - case QFont::OldEnglish: - hint = FF_DECORATIVE; - break; - case QFont::System: - hint = FF_MODERN; - break; - default: - break; - } - - lf.lfPitchAndFamily = DEFAULT_PITCH | hint; - - QString fam = faceName; - if (fam.isEmpty()) - fam = request.families.size() > 0 ? request.families.at(0) : request.family; - if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) { - qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam)); - fam.truncate(LF_FACESIZE - 1); - } - - if (fam.isEmpty()) - fam = QStringLiteral("MS Sans Serif"); - - if (fam == QLatin1String("MS Sans Serif") - && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) { - fam = QStringLiteral("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale - } - if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap)) - fam = QStringLiteral("Courier New"); - - memcpy(lf.lfFaceName, fam.utf16(), fam.size() * sizeof(wchar_t)); - - return lf; -} - QStringList QWindowsFontDatabase::extraTryFontsForFamily(const QString &family) { QStringList result; @@ -1947,7 +1241,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q } #if !defined(QT_NO_DIRECTWRITE) - if (initDirectWrite(data.data())) { + if (data->directWriteFactory != nullptr) { const QString fam = QString::fromWCharArray(lf.lfFaceName); const QString nameSubstitute = QWindowsFontEngineDirectWrite::fontNameSubstitute(fam); if (nameSubstitute != fam) { @@ -2023,62 +1317,6 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q return fe; } -QFont QWindowsFontDatabase::systemDefaultFont() -{ -#if QT_VERSION >= 0x060000 - // Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610) - NONCLIENTMETRICS ncm; - ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0); - const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont); -#else - LOGFONT lf; - GetObject(QWindowsFontDatabase::systemFont(), sizeof(lf), &lf); - QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(lf); - // "MS Shell Dlg 2" is the correct system font >= Win2k - if (systemFont.family() == QLatin1String("MS Shell Dlg")) - systemFont.setFamily(QStringLiteral("MS Shell Dlg 2")); - // Qt 5 by (Qt 4) legacy uses GetStockObject(DEFAULT_GUI_FONT) to - // obtain the default GUI font (typically "MS Shell Dlg 2, 8pt"). This has been - // long deprecated; the message font of the NONCLIENTMETRICS structure obtained by - // SystemParametersInfo(SPI_GETNONCLIENTMETRICS) should be used instead (see - // QWindowsTheme::refreshFonts(), typically "Segoe UI, 9pt"), which is larger. -#endif // Qt 5 - qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont; - return systemFont; -} - -QFont QWindowsFontDatabase::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In) -{ - if (verticalDPI_In <= 0) - verticalDPI_In = defaultVerticalDPI(); - QFont qFont(QString::fromWCharArray(logFont.lfFaceName)); - qFont.setItalic(logFont.lfItalic); - if (logFont.lfWeight != FW_DONTCARE) - qFont.setWeight(QPlatformFontDatabase::weightFromInteger(logFont.lfWeight)); - const qreal logFontHeight = qAbs(logFont.lfHeight); - qFont.setPointSizeF(logFontHeight * 72.0 / qreal(verticalDPI_In)); - qFont.setUnderline(logFont.lfUnderline); - qFont.setOverline(false); - qFont.setStrikeOut(logFont.lfStrikeOut); - return qFont; -} - -int QWindowsFontDatabase::defaultVerticalDPI() -{ - static int vDPI = -1; - if (vDPI == -1) { - if (HDC defaultDC = GetDC(0)) { - vDPI = GetDeviceCaps(defaultDC, LOGPIXELSY); - ReleaseDC(0, defaultDC); - } else { - // FIXME: Resolve now or return 96 and keep unresolved? - vDPI = 96; - } - } - return vDPI; -} - bool QWindowsFontDatabase::isPrivateFontFamily(const QString &family) const { return m_eudcFonts.contains(family) || QPlatformFontDatabase::isPrivateFontFamily(family); diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h index f132e69d4d..a76fdc0810 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h @@ -51,39 +51,16 @@ // We mean it. // +#include "qwindowsfontdatabasebase_p.h" + #include <qpa/qplatformfontdatabase.h> #include <QtCore/QSharedPointer> #include <QtCore/QLoggingCategory> #include <QtCore/qt_windows.h> -#if !defined(QT_NO_DIRECTWRITE) - struct IDWriteFactory; - struct IDWriteGdiInterop; -#endif - QT_BEGIN_NAMESPACE -Q_DECLARE_LOGGING_CATEGORY(lcQpaFonts) - -class QWindowsFontEngineData -{ - Q_DISABLE_COPY_MOVE(QWindowsFontEngineData) -public: - QWindowsFontEngineData(); - ~QWindowsFontEngineData(); - - uint pow_gamma[256]; - - bool clearTypeEnabled = false; - qreal fontSmoothingGamma; - HDC hdc = 0; -#if !defined(QT_NO_DIRECTWRITE) - IDWriteFactory *directWriteFactory = nullptr; - IDWriteGdiInterop *directWriteGdiInterop = nullptr; -#endif -}; - -class QWindowsFontDatabase : public QPlatformFontDatabase +class QWindowsFontDatabase : public QWindowsFontDatabaseBase { Q_DISABLE_COPY_MOVE(QWindowsFontDatabase) public: @@ -113,23 +90,15 @@ public: void refUniqueFont(const QString &uniqueFont); bool isPrivateFontFamily(const QString &family) const override; - static QFont systemDefaultFont(); - static QFontEngine *createEngine(const QFontDef &request, const QString &faceName, int dpi, const QSharedPointer<QWindowsFontEngineData> &data); - static HFONT systemFont(); - static QFont LOGFONT_to_QFont(const LOGFONT& lf, int verticalDPI = 0); - static qreal fontSmoothingGamma(); - static LOGFONT fontDefToLOGFONT(const QFontDef &fontDef, const QString &faceName); static QStringList extraTryFontsForFamily(const QString &family); static QString familyForStyleHint(QFont::StyleHint styleHint); - static int defaultVerticalDPI(); - static void setFontOptions(unsigned options); static unsigned fontOptions(); diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase.cpp new file mode 100644 index 0000000000..ff46866cb0 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase.cpp @@ -0,0 +1,861 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsfontdatabasebase_p.h" +#include "qwindowsfontdatabase_p.h" + +#include <QtCore/private/qsystemlibrary_p.h> +#include <QtCore/QThreadStorage> +#include <QtCore/QtEndian> + +#if !defined(QT_NO_DIRECTWRITE) +# if defined(QT_USE_DIRECTWRITE3) +# include <dwrite_3.h> +# elif defined(QT_USE_DIRECTWRITE2) +# include <dwrite_2.h> +# else +# include <dwrite.h> +# endif +# include <d2d1.h> +# include "qwindowsfontenginedirectwrite_p.h" +#endif + +QT_BEGIN_NAMESPACE + +// Helper classes for creating font engines directly from font data +namespace { + +# pragma pack(1) + + // Common structure for all formats of the "name" table + struct NameTable + { + quint16 format; + quint16 count; + quint16 stringOffset; + }; + + struct NameRecord + { + quint16 platformID; + quint16 encodingID; + quint16 languageID; + quint16 nameID; + quint16 length; + quint16 offset; + }; + + struct OffsetSubTable + { + quint32 scalerType; + quint16 numTables; + quint16 searchRange; + quint16 entrySelector; + quint16 rangeShift; + }; + + struct TableDirectory : public QWindowsFontDatabaseBase::FontTable + { + quint32 identifier; + quint32 checkSum; + quint32 offset; + quint32 length; + }; + + struct OS2Table + { + quint16 version; + qint16 avgCharWidth; + quint16 weightClass; + quint16 widthClass; + quint16 type; + qint16 subscriptXSize; + qint16 subscriptYSize; + qint16 subscriptXOffset; + qint16 subscriptYOffset; + qint16 superscriptXSize; + qint16 superscriptYSize; + qint16 superscriptXOffset; + qint16 superscriptYOffset; + qint16 strikeOutSize; + qint16 strikeOutPosition; + qint16 familyClass; + quint8 panose[10]; + quint32 unicodeRanges[4]; + quint8 vendorID[4]; + quint16 selection; + quint16 firstCharIndex; + quint16 lastCharIndex; + qint16 typoAscender; + qint16 typoDescender; + qint16 typoLineGap; + quint16 winAscent; + quint16 winDescent; + quint32 codepageRanges[2]; + qint16 height; + qint16 capHeight; + quint16 defaultChar; + quint16 breakChar; + quint16 maxContext; + }; + +# pragma pack() + +} // Anonymous namespace + +QWindowsFontDatabaseBase::FontTable *QWindowsFontDatabaseBase::EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName) +{ + Q_ASSERT(tagName.size() == 4); + quint32 tagId = *(reinterpret_cast<const quint32 *>(tagName.constData())); + const size_t fontDataSize = m_fontData.size(); + if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable))) + return nullptr; + + OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data()); + TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1); + + const size_t tableCount = qFromBigEndian<quint16>(offsetSubTable->numTables); + if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable) + sizeof(TableDirectory) * tableCount)) + return nullptr; + + TableDirectory *tableDirectoryEnd = tableDirectory + tableCount; + for (TableDirectory *entry = tableDirectory; entry < tableDirectoryEnd; ++entry) { + if (entry->identifier == tagId) + return entry; + } + + return nullptr; +} + +QString QWindowsFontDatabaseBase::EmbeddedFont::familyName(QWindowsFontDatabaseBase::FontTable *directoryEntry) +{ + QString name; + + TableDirectory *nameTableDirectoryEntry = static_cast<TableDirectory *>(directoryEntry); + if (nameTableDirectoryEntry == nullptr) + nameTableDirectoryEntry = static_cast<TableDirectory *>(tableDirectoryEntry("name")); + + if (nameTableDirectoryEntry != nullptr) { + quint32 offset = qFromBigEndian<quint32>(nameTableDirectoryEntry->offset); + if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameTable))) + return QString(); + + NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() + offset); + NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); + + quint16 nameTableCount = qFromBigEndian<quint16>(nameTable->count); + if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameRecord) * nameTableCount)) + return QString(); + + for (int i = 0; i < nameTableCount; ++i, ++nameRecord) { + if (qFromBigEndian<quint16>(nameRecord->nameID) == 1 + && qFromBigEndian<quint16>(nameRecord->platformID) == 3 // Windows + && qFromBigEndian<quint16>(nameRecord->languageID) == 0x0409) { // US English + quint16 stringOffset = qFromBigEndian<quint16>(nameTable->stringOffset); + quint16 nameOffset = qFromBigEndian<quint16>(nameRecord->offset); + quint16 nameLength = qFromBigEndian<quint16>(nameRecord->length); + + if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + stringOffset + nameOffset + nameLength)) + return QString(); + + const void *ptr = reinterpret_cast<const quint8 *>(nameTable) + + stringOffset + + nameOffset; + + const quint16 *s = reinterpret_cast<const quint16 *>(ptr); + const quint16 *e = s + nameLength / sizeof(quint16); + while (s != e) + name += QChar( qFromBigEndian<quint16>(*s++)); + break; + } + } + } + + return name; +} + +void QWindowsFontDatabaseBase::EmbeddedFont::updateFromOS2Table(QFontEngine *fontEngine) +{ + TableDirectory *os2TableEntry = static_cast<TableDirectory *>(tableDirectoryEntry("OS/2")); + if (os2TableEntry != nullptr) { + const OS2Table *os2Table = + reinterpret_cast<const OS2Table *>(m_fontData.constData() + + qFromBigEndian<quint32>(os2TableEntry->offset)); + + bool italic = qFromBigEndian<quint16>(os2Table->selection) & (1 << 0); + bool oblique = qFromBigEndian<quint16>(os2Table->selection) & (1 << 9); + + if (italic) + fontEngine->fontDef.style = QFont::StyleItalic; + else if (oblique) + fontEngine->fontDef.style = QFont::StyleOblique; + else + fontEngine->fontDef.style = QFont::StyleNormal; + + fontEngine->fontDef.weight = QPlatformFontDatabase::weightFromInteger(qFromBigEndian<quint16>(os2Table->weightClass)); + } +} + +QString QWindowsFontDatabaseBase::EmbeddedFont::changeFamilyName(const QString &newFamilyName) +{ + TableDirectory *nameTableDirectoryEntry = static_cast<TableDirectory *>(tableDirectoryEntry("name")); + if (nameTableDirectoryEntry == nullptr) + return QString(); + + QString oldFamilyName = familyName(nameTableDirectoryEntry); + + // Reserve size for name table header, five required name records and string + const int requiredRecordCount = 5; + quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 }; + + int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount; + int newFamilyNameSize = newFamilyName.size() * int(sizeof(quint16)); + + const QString regularString = QString::fromLatin1("Regular"); + int regularStringSize = regularString.size() * int(sizeof(quint16)); + + // Align table size of table to 32 bits (pad with 0) + int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4; + + QByteArray newNameTable(fullSize, char(0)); + + { + NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data()); + nameTable->count = qbswap<quint16>(requiredRecordCount); + nameTable->stringOffset = qbswap<quint16>(sizeOfHeader); + + NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); + for (int i = 0; i < requiredRecordCount; ++i, nameRecord++) { + nameRecord->nameID = qbswap<quint16>(nameIds[i]); + nameRecord->encodingID = qbswap<quint16>(1); + nameRecord->languageID = qbswap<quint16>(0x0409); + nameRecord->platformID = qbswap<quint16>(3); + nameRecord->length = qbswap<quint16>(newFamilyNameSize); + + // Special case for sub-family + if (nameIds[i] == 4) { + nameRecord->offset = qbswap<quint16>(newFamilyNameSize); + nameRecord->length = qbswap<quint16>(regularStringSize); + } + } + + // nameRecord now points to string data + quint16 *stringStorage = reinterpret_cast<quint16 *>(nameRecord); + const quint16 *sourceString = newFamilyName.utf16(); + for (int i = 0; i < newFamilyName.size(); ++i) + stringStorage[i] = qbswap<quint16>(sourceString[i]); + stringStorage += newFamilyName.size(); + + sourceString = regularString.utf16(); + for (int i = 0; i < regularString.size(); ++i) + stringStorage[i] = qbswap<quint16>(sourceString[i]); + } + + quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data()); + quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize); + + quint32 checkSum = 0; + while (p < tableEnd) + checkSum += qFromBigEndian<quint32>(*(p++)); + + nameTableDirectoryEntry->checkSum = qbswap<quint32>(checkSum); + nameTableDirectoryEntry->offset = qbswap<quint32>(m_fontData.size()); + nameTableDirectoryEntry->length = qbswap<quint32>(fullSize); + + m_fontData.append(newNameTable); + + return oldFamilyName; +} + +#if !defined(QT_NO_DIRECTWRITE) + +namespace { + class DirectWriteFontFileStream: public IDWriteFontFileStream + { + Q_DISABLE_COPY(DirectWriteFontFileStream) + public: + DirectWriteFontFileStream(const QByteArray &fontData) + : m_fontData(fontData) + , m_referenceCount(0) + { + } + virtual ~DirectWriteFontFileStream() + { + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset, + UINT64 fragmentSize, OUT void **fragmentContext); + void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext); + HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize); + HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime); + + private: + QByteArray m_fontData; + ULONG m_referenceCount; + }; + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) { + *object = this; + AddRef(); + return S_OK; + } else { + *object = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef() + { + return InterlockedIncrement(&m_referenceCount); + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release() + { + ULONG newCount = InterlockedDecrement(&m_referenceCount); + if (newCount == 0) + delete this; + return newCount; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment( + const void **fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + OUT void **fragmentContext) + { + *fragmentContext = NULL; + if (fileOffset + fragmentSize <= quint64(m_fontData.size())) { + *fragmentStart = m_fontData.data() + fileOffset; + return S_OK; + } else { + *fragmentStart = NULL; + return E_FAIL; + } + } + + void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *) + { + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize) + { + *fileSize = m_fontData.size(); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) + { + *lastWriteTime = 0; + return E_NOTIMPL; + } + + class DirectWriteFontFileLoader: public IDWriteFontFileLoader + { + public: + DirectWriteFontFileLoader() : m_referenceCount(0) {} + virtual ~DirectWriteFontFileLoader() + { + } + + inline void addKey(const void *key, const QByteArray &fontData) + { + Q_ASSERT(!m_fontDatas.contains(key)); + m_fontDatas.insert(key, fontData); + } + + inline void removeKey(const void *key) + { + m_fontDatas.remove(key); + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + OUT IDWriteFontFileStream **fontFileStream); + + private: + ULONG m_referenceCount; + QHash<const void *, QByteArray> m_fontDatas; + }; + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid, + void **object) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { + *object = this; + AddRef(); + return S_OK; + } else { + *object = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef() + { + return InterlockedIncrement(&m_referenceCount); + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release() + { + ULONG newCount = InterlockedDecrement(&m_referenceCount); + if (newCount == 0) + delete this; + return newCount; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey( + void const *fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + IDWriteFontFileStream **fontFileStream) + { + Q_UNUSED(fontFileReferenceKeySize); + + if (fontFileReferenceKeySize != sizeof(const void *)) { + qWarning("%s: Wrong key size", __FUNCTION__); + return E_FAIL; + } + + const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey); + *fontFileStream = NULL; + auto it = m_fontDatas.constFind(key); + if (it == m_fontDatas.constEnd()) + return E_FAIL; + + QByteArray fontData = it.value(); + DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData); + stream->AddRef(); + *fontFileStream = stream; + + return S_OK; + } + + class CustomFontFileLoader + { + public: + CustomFontFileLoader(IDWriteFactory *factory) + { + m_directWriteFactory = factory; + + if (m_directWriteFactory) { + m_directWriteFactory->AddRef(); + + m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); + m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); + } + } + + ~CustomFontFileLoader() + { + if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr) + m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); + + if (m_directWriteFactory != nullptr) + m_directWriteFactory->Release(); + } + + void addKey(const void *key, const QByteArray &fontData) + { + if (m_directWriteFontFileLoader != nullptr) + m_directWriteFontFileLoader->addKey(key, fontData); + } + + void removeKey(const void *key) + { + if (m_directWriteFontFileLoader != nullptr) + m_directWriteFontFileLoader->removeKey(key); + } + + IDWriteFontFileLoader *loader() const + { + return m_directWriteFontFileLoader; + } + + private: + IDWriteFactory *m_directWriteFactory = nullptr; + DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr; + }; +} // Anonymous namespace + +#endif // !defined(QT_NO_DIRECTWRITE) + + +QWindowsFontEngineData::~QWindowsFontEngineData() +{ + if (hdc) + DeleteDC(hdc); + +#if !defined(QT_NO_DIRECTWRITE) + if (directWriteGdiInterop) + directWriteGdiInterop->Release(); + if (directWriteFactory) + directWriteFactory->Release(); +#endif +} + +QWindowsFontDatabaseBase::QWindowsFontDatabaseBase() +{ +} + +QWindowsFontDatabaseBase::~QWindowsFontDatabaseBase() +{ +} + +typedef QSharedPointer<QWindowsFontEngineData> QWindowsFontEngineDataPtr; +typedef QThreadStorage<QWindowsFontEngineDataPtr> FontEngineThreadLocalData; +Q_GLOBAL_STATIC(FontEngineThreadLocalData, fontEngineThreadLocalData) + +QSharedPointer<QWindowsFontEngineData> QWindowsFontDatabaseBase::data() +{ + FontEngineThreadLocalData *data = fontEngineThreadLocalData(); + if (!data->hasLocalData()) + data->setLocalData(QSharedPointer<QWindowsFontEngineData>::create()); + + if (!init(data->localData())) + qCWarning(lcQpaFonts) << "Cannot initialize common font database data"; + + return data->localData(); +} + +bool QWindowsFontDatabaseBase::init(QSharedPointer<QWindowsFontEngineData> d) +{ + if (!d->directWriteFactory) { + createDirectWriteFactory(&d->directWriteFactory); + if (!d->directWriteFactory) + return false; + } + if (!d->directWriteGdiInterop) { + const HRESULT hr = d->directWriteFactory->GetGdiInterop(&d->directWriteGdiInterop); + if (FAILED(hr)) { + qErrnoWarning("%s: GetGdiInterop failed", __FUNCTION__); + return false; + } + } + return true; +} + +// ### Qt 6: Link directly to dwrite instead +typedef HRESULT (WINAPI *DWriteCreateFactoryType)(DWRITE_FACTORY_TYPE, const IID &, IUnknown **); +static inline DWriteCreateFactoryType resolveDWriteCreateFactory() +{ + QSystemLibrary library(QStringLiteral("dwrite")); + QFunctionPointer result = library.resolve("DWriteCreateFactory"); + if (Q_UNLIKELY(!result)) { + qWarning("Unable to load dwrite.dll"); + return nullptr; + } + return reinterpret_cast<DWriteCreateFactoryType>(result); +} + +void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory) +{ + *factory = nullptr; + + static const DWriteCreateFactoryType dWriteCreateFactory = resolveDWriteCreateFactory(); + if (!dWriteCreateFactory) + return; + + IUnknown *result = nullptr; +#if defined(QT_USE_DIRECTWRITE3) + dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result); +#endif + +#if defined(QT_USE_DIRECTWRITE2) + if (result == nullptr) + dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result); +#endif + + if (result == nullptr) { + if (FAILED(dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) { + qErrnoWarning("DWriteCreateFactory failed"); + return; + } + } + + *factory = static_cast<IDWriteFactory *>(result); +} + +int QWindowsFontDatabaseBase::defaultVerticalDPI() +{ + static int vDPI = -1; + if (vDPI == -1) { + if (HDC defaultDC = GetDC(0)) { + vDPI = GetDeviceCaps(defaultDC, LOGPIXELSY); + ReleaseDC(0, defaultDC); + } else { + // FIXME: Resolve now or return 96 and keep unresolved? + vDPI = 96; + } + } + return vDPI; +} + +LOGFONT QWindowsFontDatabaseBase::fontDefToLOGFONT(const QFontDef &request, const QString &faceName) +{ + LOGFONT lf; + memset(&lf, 0, sizeof(LOGFONT)); + + lf.lfHeight = -qRound(request.pixelSize); + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + if (request.weight == 50) + lf.lfWeight = FW_DONTCARE; + else + lf.lfWeight = (request.weight*900)/99; + lf.lfItalic = request.style != QFont::StyleNormal; + lf.lfCharSet = DEFAULT_CHARSET; + + int strat = OUT_DEFAULT_PRECIS; + if (request.styleStrategy & QFont::PreferBitmap) { + strat = OUT_RASTER_PRECIS; + } else if (request.styleStrategy & QFont::PreferDevice) { + strat = OUT_DEVICE_PRECIS; + } else if (request.styleStrategy & QFont::PreferOutline) { + strat = OUT_OUTLINE_PRECIS; + } else if (request.styleStrategy & QFont::ForceOutline) { + strat = OUT_TT_ONLY_PRECIS; + } + + lf.lfOutPrecision = strat; + + int qual = DEFAULT_QUALITY; + + if (request.styleStrategy & QFont::PreferMatch) + qual = DRAFT_QUALITY; + else if (request.styleStrategy & QFont::PreferQuality) + qual = PROOF_QUALITY; + + if (request.styleStrategy & QFont::PreferAntialias) { + qual = (request.styleStrategy & QFont::NoSubpixelAntialias) == 0 + ? CLEARTYPE_QUALITY : ANTIALIASED_QUALITY; + } else if (request.styleStrategy & QFont::NoAntialias) { + qual = NONANTIALIASED_QUALITY; + } else if ((request.styleStrategy & QFont::NoSubpixelAntialias) && data()->clearTypeEnabled) { + qual = ANTIALIASED_QUALITY; + } + + lf.lfQuality = qual; + + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + + int hint = FF_DONTCARE; + switch (request.styleHint) { + case QFont::Helvetica: + hint = FF_SWISS; + break; + case QFont::Times: + hint = FF_ROMAN; + break; + case QFont::Courier: + hint = FF_MODERN; + break; + case QFont::OldEnglish: + hint = FF_DECORATIVE; + break; + case QFont::System: + hint = FF_MODERN; + break; + default: + break; + } + + lf.lfPitchAndFamily = DEFAULT_PITCH | hint; + + QString fam = faceName; + if (fam.isEmpty()) + fam = request.families.size() > 0 ? request.families.at(0) : request.family; + if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) { + qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam)); + fam.truncate(LF_FACESIZE - 1); + } + + if (fam.isEmpty()) + fam = QStringLiteral("MS Sans Serif"); + + if (fam == QLatin1String("MS Sans Serif") + && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) { + fam = QStringLiteral("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale + } + if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap)) + fam = QStringLiteral("Courier New"); + + memcpy(lf.lfFaceName, fam.utf16(), fam.size() * sizeof(wchar_t)); + + return lf; +} + +QFont QWindowsFontDatabaseBase::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In) +{ + if (verticalDPI_In <= 0) + verticalDPI_In = defaultVerticalDPI(); + QFont qFont(QString::fromWCharArray(logFont.lfFaceName)); + qFont.setItalic(logFont.lfItalic); + if (logFont.lfWeight != FW_DONTCARE) + qFont.setWeight(QPlatformFontDatabase::weightFromInteger(logFont.lfWeight)); + const qreal logFontHeight = qAbs(logFont.lfHeight); + qFont.setPointSizeF(logFontHeight * 72.0 / qreal(verticalDPI_In)); + qFont.setUnderline(logFont.lfUnderline); + qFont.setOverline(false); + qFont.setStrikeOut(logFont.lfStrikeOut); + return qFont; +} + +// ### fixme Qt 6 (QTBUG-58610): See comment at QWindowsFontDatabase::systemDefaultFont() +HFONT QWindowsFontDatabaseBase::systemFont() +{ + static const auto stock_sysfont = + reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT)); + return stock_sysfont; +} + +QFont QWindowsFontDatabaseBase::systemDefaultFont() +{ +#if QT_VERSION >= 0x060000 + // Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610) + NONCLIENTMETRICS ncm; + ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0); + const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont); +#else + LOGFONT lf; + GetObject(systemFont(), sizeof(lf), &lf); + QFont systemFont = LOGFONT_to_QFont(lf); + // "MS Shell Dlg 2" is the correct system font >= Win2k + if (systemFont.family() == QLatin1String("MS Shell Dlg")) + systemFont.setFamily(QStringLiteral("MS Shell Dlg 2")); + // Qt 5 by (Qt 4) legacy uses GetStockObject(DEFAULT_GUI_FONT) to + // obtain the default GUI font (typically "MS Shell Dlg 2, 8pt"). This has been + // long deprecated; the message font of the NONCLIENTMETRICS structure obtained by + // SystemParametersInfo(SPI_GETNONCLIENTMETRICS) should be used instead (see + // QWindowsTheme::refreshFonts(), typically "Segoe UI, 9pt"), which is larger. +#endif // Qt 5 + qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont; + return systemFont; +} + +#if !defined(QT_NO_DIRECTWRITE) +IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData) const +{ + QSharedPointer<QWindowsFontEngineData> fontEngineData = data(); + if (fontEngineData->directWriteFactory == nullptr) { + qCWarning(lcQpaFonts) << "DirectWrite factory not created in QWindowsFontDatabaseBase::createDirectWriteFace()"; + return nullptr; + } + + CustomFontFileLoader fontFileLoader(fontEngineData->directWriteFactory); + fontFileLoader.addKey(this, fontData); + + IDWriteFontFile *fontFile = nullptr; + const void *key = this; + + HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key, + sizeof(void *), + fontFileLoader.loader(), + &fontFile); + if (FAILED(hres)) { + qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__); + return nullptr; + } + + BOOL isSupportedFontType; + DWRITE_FONT_FILE_TYPE fontFileType; + DWRITE_FONT_FACE_TYPE fontFaceType; + UINT32 numberOfFaces; + fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces); + if (!isSupportedFontType) { + fontFile->Release(); + return nullptr; + } + + // ### Currently no support for .ttc, but we could easily return a list here. + IDWriteFontFace *directWriteFontFace = nullptr; + hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType, + 1, + &fontFile, + 0, + DWRITE_FONT_SIMULATIONS_NONE, + &directWriteFontFace); + if (FAILED(hres)) { + qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__); + fontFile->Release(); + return nullptr; + } + + fontFile->Release(); + return directWriteFontFace; +} +#endif // !defined(QT_NO_DIRECTWRITE) + +QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +{ + QFontEngine *fontEngine = nullptr; + +#if !defined(QT_NO_DIRECTWRITE) + QSharedPointer<QWindowsFontEngineData> fontEngineData = data(); + if (fontEngineData->directWriteFactory == nullptr) + return nullptr; + + IDWriteFontFace *directWriteFontFace = createDirectWriteFace(fontData); + fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace, + pixelSize, + fontEngineData); + + // Get font family from font data + EmbeddedFont font(fontData); + font.updateFromOS2Table(fontEngine); + fontEngine->fontDef.family = font.familyName(); + fontEngine->fontDef.hintingPreference = hintingPreference; + + directWriteFontFace->Release(); +#endif // !defined(QT_NO_DIRECTWRITE) + + return fontEngine; +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase_p.h b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase_p.h new file mode 100644 index 0000000000..004889ab86 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSFONTDATABASEBASE_P_H +#define QWINDOWSFONTDATABASEBASE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qpa/qplatformfontdatabase.h> +#include <QtCore/QSharedPointer> +#include <QtCore/QLoggingCategory> +#include <QtCore/qt_windows.h> + +#if !defined(QT_NO_DIRECTWRITE) + struct IDWriteFactory; + struct IDWriteGdiInterop; + struct IDWriteFontFace; +#endif + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQpaFonts); + +class QWindowsFontEngineData +{ + Q_DISABLE_COPY_MOVE(QWindowsFontEngineData) +public: + QWindowsFontEngineData(); + ~QWindowsFontEngineData(); + + uint pow_gamma[256]; + + bool clearTypeEnabled = false; + qreal fontSmoothingGamma; + HDC hdc = 0; +#if !defined(QT_NO_DIRECTWRITE) + IDWriteFactory *directWriteFactory = nullptr; + IDWriteGdiInterop *directWriteGdiInterop = nullptr; +#endif +}; + +class QWindowsFontDatabaseBase : public QPlatformFontDatabase +{ +public: + QWindowsFontDatabaseBase(); + ~QWindowsFontDatabaseBase() override; + + QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override; + + static int defaultVerticalDPI(); + static QSharedPointer<QWindowsFontEngineData> data(); + static void createDirectWriteFactory(IDWriteFactory **factory); + static QFont systemDefaultFont(); + static HFONT systemFont(); + static LOGFONT fontDefToLOGFONT(const QFontDef &fontDef, const QString &faceName); + static QFont LOGFONT_to_QFont(const LOGFONT& lf, int verticalDPI = 0); + + class FontTable{}; + class EmbeddedFont + { + public: + EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) {} + + QString changeFamilyName(const QString &newFamilyName); + QByteArray data() const { return m_fontData; } + void updateFromOS2Table(QFontEngine *fontEngine); + FontTable *tableDirectoryEntry(const QByteArray &tagName); + QString familyName(FontTable *nameTableDirectory = nullptr); + + private: + QByteArray m_fontData; + }; + +protected: + +#if !defined(QT_NO_DIRECTWRITE) + IDWriteFontFace *createDirectWriteFace(const QByteArray &fontData) const; +#endif + +private: + static bool init(QSharedPointer<QWindowsFontEngineData> data); +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSFONTDATABASEBASE_P_H diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp index 5c999f455e..45ce396d5e 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp @@ -187,6 +187,18 @@ namespace { } +static DWRITE_MEASURING_MODE renderModeToMeasureMode(DWRITE_RENDERING_MODE renderMode) +{ + switch (renderMode) { + case DWRITE_RENDERING_MODE_GDI_CLASSIC: + return DWRITE_MEASURING_MODE_GDI_CLASSIC; + case DWRITE_RENDERING_MODE_GDI_NATURAL: + return DWRITE_MEASURING_MODE_GDI_NATURAL; + default: + return DWRITE_MEASURING_MODE_NATURAL; + } +} + static DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(QFont::HintingPreference hintingPreference) { if (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting) @@ -209,13 +221,10 @@ static DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(QFont::HintingPref \ingroup qt-lighthouse-win Font engine for subpixel positioned text on Windows Vista - (with platform update) and Windows 7. If selected during + (with platform update) and later. If selected during configuration, the engine will be selected only when the hinting - preference of a font is set to None or Vertical hinting. The font - database uses most of the same logic but creates a direct write - font based on the LOGFONT rather than a GDI handle. - - Will probably be superseded by a common Free Type font engine in Qt 5.X. + preference of a font is set to None or Vertical hinting, or + when fontengine=directwrite is selected as platform option. */ QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace, @@ -480,9 +489,22 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn glyphIndices[i] = UINT16(glyphs->glyphs[i]); QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size()); - HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(), - glyphIndices.size(), - glyphMetrics.data()); + + HRESULT hr; + DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference)); + if (renderMode == DWRITE_RENDERING_MODE_GDI_CLASSIC || renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL) { + hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(float(fontDef.pixelSize), + 1.0f, + NULL, + TRUE, + glyphIndices.data(), + glyphIndices.size(), + glyphMetrics.data()); + } else { + hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(), + glyphIndices.size(), + glyphMetrics.data()); + } if (SUCCEEDED(hr)) { qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0; for (int i = 0; i < glyphs->numGlyphs; ++i) @@ -688,6 +710,8 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference)); + DWRITE_MEASURING_MODE measureMode = + renderModeToMeasureMode(renderMode); IDWriteGlyphRunAnalysis *glyphAnalysis = NULL; HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis( @@ -695,7 +719,7 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, 1.0f, &transform, renderMode, - DWRITE_MEASURING_MODE_NATURAL, + measureMode, 0.0, 0.0, &glyphAnalysis ); @@ -725,7 +749,7 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, 0.0f, &glyphRun, NULL, - DWRITE_MEASURING_MODE_NATURAL, + measureMode, NULL, 0, &enumerator); @@ -756,7 +780,7 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, 1.0f, &transform, renderMode, - DWRITE_MEASURING_MODE_NATURAL, + measureMode, 0.0, 0.0, &colorGlyphsAnalysis ); @@ -996,6 +1020,7 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference)); + DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode); IDWriteGlyphRunAnalysis *glyphAnalysis = NULL; HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis( @@ -1003,7 +1028,7 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph 1.0f, &transform, renderMode, - DWRITE_MEASURING_MODE_NATURAL, + measureMode, 0.0, 0.0, &glyphAnalysis ); diff --git a/src/platformsupport/fontdatabases/windows/windows.pri b/src/platformsupport/fontdatabases/windows/windows.pri index 7ddfb2c281..fbe6d490f9 100644 --- a/src/platformsupport/fontdatabases/windows/windows.pri +++ b/src/platformsupport/fontdatabases/windows/windows.pri @@ -2,11 +2,13 @@ QT *= gui-private SOURCES += \ $$PWD/qwindowsfontdatabase.cpp \ + $$PWD/qwindowsfontdatabasebase.cpp \ $$PWD/qwindowsfontengine.cpp \ $$PWD/qwindowsnativeimage.cpp HEADERS += \ $$PWD/qwindowsfontdatabase_p.h \ + $$PWD/qwindowsfontdatabasebase_p.h \ $$PWD/qwindowsfontengine_p.h \ $$PWD/qwindowsnativeimage_p.h @@ -16,7 +18,13 @@ qtConfig(freetype) { } qtConfig(directwrite):qtConfig(direct2d) { - qtConfig(directwrite2) { + qtConfig(directwrite3) { + QMAKE_USE_PRIVATE += dwrite_3 + DEFINES *= QT_USE_DIRECTWRITE3 QT_USE_DIRECTWRITE2 + + SOURCES += $$PWD/qwindowsdirectwritefontdatabase.cpp + HEADERS += $$PWD/qwindowsdirectwritefontdatabase_p.h + } else: qtConfig(directwrite2) { QMAKE_USE_PRIVATE += dwrite_2 DEFINES *= QT_USE_DIRECTWRITE2 } else { diff --git a/src/platformsupport/platformcompositor/platformcompositor.pro b/src/platformsupport/platformcompositor/platformcompositor.pro index 81c31571d0..89386ab3ef 100644 --- a/src/platformsupport/platformcompositor/platformcompositor.pro +++ b/src/platformsupport/platformcompositor/platformcompositor.pro @@ -1,16 +1,18 @@ TARGET = QtPlatformCompositorSupport MODULE = platformcompositor_support -QT = core-private gui-private +QT = core-private gui-private opengl CONFIG += static internal_module DEFINES += QT_NO_CAST_FROM_ASCII SOURCES += \ + qplatformbackingstoreopenglsupport.cpp \ qopenglcompositor.cpp \ qopenglcompositorbackingstore.cpp HEADERS += \ + qplatformbackingstoreopenglsupport.h \ qopenglcompositor_p.h \ qopenglcompositorbackingstore_p.h diff --git a/src/platformsupport/platformcompositor/qopenglcompositor_p.h b/src/platformsupport/platformcompositor/qopenglcompositor_p.h index 41a3288240..c9414c82c6 100644 --- a/src/platformsupport/platformcompositor/qopenglcompositor_p.h +++ b/src/platformsupport/platformcompositor/qopenglcompositor_p.h @@ -52,7 +52,7 @@ // #include <QtCore/QTimer> -#include <QtGui/QOpenGLTextureBlitter> +#include <QtOpenGL/QOpenGLTextureBlitter> #include <QtGui/QMatrix4x4> QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/platformcompositor/qplatformbackingstoreopenglsupport.cpp b/src/platformsupport/platformcompositor/qplatformbackingstoreopenglsupport.cpp new file mode 100644 index 0000000000..ca50910114 --- /dev/null +++ b/src/platformsupport/platformcompositor/qplatformbackingstoreopenglsupport.cpp @@ -0,0 +1,455 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_OPENGL + +#include "qplatformbackingstoreopenglsupport.h" + +#include <QtGui/private/qwindow_p.h> + +#include <qpa/qplatformgraphicsbuffer.h> +#include <qpa/qplatformgraphicsbufferhelper.h> + +#include <QtOpenGL/QOpenGLTextureBlitter> +#include <QtGui/qopengl.h> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOffscreenSurface> + +#ifndef GL_TEXTURE_BASE_LEVEL +#define GL_TEXTURE_BASE_LEVEL 0x813C +#endif +#ifndef GL_TEXTURE_MAX_LEVEL +#define GL_TEXTURE_MAX_LEVEL 0x813D +#endif +#ifndef GL_UNPACK_ROW_LENGTH +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#endif +#ifndef GL_RGB10_A2 +#define GL_RGB10_A2 0x8059 +#endif +#ifndef GL_UNSIGNED_INT_2_10_10_10_REV +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#endif + +#ifndef GL_FRAMEBUFFER_SRGB +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#endif +#ifndef GL_FRAMEBUFFER_SRGB_CAPABLE +#define GL_FRAMEBUFFER_SRGB_CAPABLE 0x8DBA +#endif + +QT_BEGIN_NAMESPACE + +static inline QRect deviceRect(const QRect &rect, QWindow *window) +{ + QRect deviceRect(rect.topLeft() * window->devicePixelRatio(), + rect.size() * window->devicePixelRatio()); + return deviceRect; +} + +static inline QPoint deviceOffset(const QPoint &pt, QWindow *window) +{ + return pt * window->devicePixelRatio(); +} + +static QRegion deviceRegion(const QRegion ®ion, QWindow *window, const QPoint &offset) +{ + if (offset.isNull() && window->devicePixelRatio() <= 1) + return region; + + QVector<QRect> rects; + rects.reserve(region.rectCount()); + for (const QRect &rect : region) + rects.append(deviceRect(rect.translated(offset), window)); + + QRegion deviceRegion; + deviceRegion.setRects(rects.constData(), rects.count()); + return deviceRegion; +} + +static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight) +{ + return QRect(topLeftRect.x(), windowHeight - topLeftRect.bottomRight().y() - 1, + topLeftRect.width(), topLeftRect.height()); +} + +static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, QWindow *window, const QRect &deviceWindowRect, + QOpenGLTextureBlitter *blitter, const QPoint &offset, bool canUseSrgb) +{ + const QRect clipRect = textures->clipRect(idx); + if (clipRect.isEmpty()) + return; + + QRect rectInWindow = textures->geometry(idx); + // relative to the TLW, not necessarily our window (if the flush is for a native child widget), have to adjust + rectInWindow.translate(-offset); + + const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft()); + const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height()); + + const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(deviceRect(clippedRectInWindow, window), + deviceWindowRect); + + const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(deviceRect(srcRect, window), + deviceRect(rectInWindow, window).size(), + QOpenGLTextureBlitter::OriginBottomLeft); + + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + const bool srgb = textures->flags(idx).testFlag(QPlatformTextureList::TextureIsSrgb); + if (srgb && canUseSrgb) + funcs->glEnable(GL_FRAMEBUFFER_SRGB); + + blitter->blit(textures->textureId(idx), target, source); + + if (srgb && canUseSrgb) + funcs->glDisable(GL_FRAMEBUFFER_SRGB); +} + +QPlatformBackingStoreOpenGLSupport::~QPlatformBackingStoreOpenGLSupport() { + if (context) { + QOffscreenSurface offscreenSurface; + offscreenSurface.setFormat(context->format()); + offscreenSurface.create(); + context->makeCurrent(&offscreenSurface); + if (textureId) + context->functions()->glDeleteTextures(1, &textureId); + if (blitter) + blitter->destroy(); + } + delete blitter; +} + +void QPlatformBackingStoreOpenGLSupport::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) +{ + if (!qt_window_private(window)->receivedExpose) + return; + + if (!context) { + context.reset(new QOpenGLContext); + context->setFormat(window->requestedFormat()); + context->setScreen(window->screen()); + context->setShareContext(qt_window_private(window)->shareContext()); + if (!context->create()) { + qCWarning(lcQpaBackingStore, "composeAndFlush: QOpenGLContext creation failed"); + return; + } + } + + bool current = context->makeCurrent(window); + + if (!current && context->isValid()) { + delete blitter; + blitter = nullptr; + textureId = 0; + current = context->create() && context->makeCurrent(window); + } + + if (!current) { + qCWarning(lcQpaBackingStore, "composeAndFlush: makeCurrent() failed"); + return; + } + + qCDebug(lcQpaBackingStore) << "Composing and flushing" << region << "of" << window + << "at offset" << offset << "with" << textures->count() << "texture(s) in" << textures; + + QWindowPrivate::get(window)->lastComposeTime.start(); + + QOpenGLFunctions *funcs = context->functions(); + funcs->glViewport(0, 0, qRound(window->width() * window->devicePixelRatio()), qRound(window->height() * window->devicePixelRatio())); + funcs->glClearColor(0, 0, 0, translucentBackground ? 0 : 1); + funcs->glClear(GL_COLOR_BUFFER_BIT); + + if (!blitter) { + blitter = new QOpenGLTextureBlitter; + blitter->create(); + } + + blitter->bind(); + + const QRect deviceWindowRect = deviceRect(QRect(QPoint(), window->size()), window); + const QPoint deviceWindowOffset = deviceOffset(offset, window); + + bool canUseSrgb = false; + // If there are any sRGB textures in the list, check if the destination + // framebuffer is sRGB capable. + for (int i = 0; i < textures->count(); ++i) { + if (textures->flags(i).testFlag(QPlatformTextureList::TextureIsSrgb)) { + GLint cap = 0; + funcs->glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE, &cap); + if (cap) + canUseSrgb = true; + break; + } + } + + // Textures for renderToTexture widgets. + for (int i = 0; i < textures->count(); ++i) { + if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) + blitTextureForWidget(textures, i, window, deviceWindowRect, blitter, offset, canUseSrgb); + } + + // Backingstore texture with the normal widgets. + GLuint textureId = 0; + QOpenGLTextureBlitter::Origin origin = QOpenGLTextureBlitter::OriginTopLeft; + if (QPlatformGraphicsBuffer *graphicsBuffer = backingStore->graphicsBuffer()) { + if (graphicsBuffer->size() != textureSize) { + if (this->textureId) + funcs->glDeleteTextures(1, &this->textureId); + funcs->glGenTextures(1, &this->textureId); + funcs->glBindTexture(GL_TEXTURE_2D, this->textureId); + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + } + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &needsSwizzle, &premultiplied)) { + textureSize = graphicsBuffer->size(); + } else { + textureSize = QSize(0,0); + } + + graphicsBuffer->unlock(); + } else if (!region.isEmpty()){ + funcs->glBindTexture(GL_TEXTURE_2D, this->textureId); + QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &needsSwizzle, &premultiplied); + graphicsBuffer->unlock(); + } + + if (graphicsBuffer->origin() == QPlatformGraphicsBuffer::OriginBottomLeft) + origin = QOpenGLTextureBlitter::OriginBottomLeft; + textureId = this->textureId; + } else { + QPlatformBackingStore::TextureFlags flags; + textureId = backingStore->toTexture(deviceRegion(region, window, offset), &textureSize, &flags); + needsSwizzle = (flags & QPlatformBackingStore::TextureSwizzle) != 0; + premultiplied = (flags & QPlatformBackingStore::TexturePremultiplied) != 0; + if (flags & QPlatformBackingStore::TextureFlip) + origin = QOpenGLTextureBlitter::OriginBottomLeft; + } + + funcs->glEnable(GL_BLEND); + if (premultiplied) + funcs->glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + else + funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + + if (textureId) { + if (needsSwizzle) + blitter->setRedBlueSwizzle(true); + // The backingstore is for the entire tlw. + // In case of native children offset tells the position relative to the tlw. + const QRect srcRect = toBottomLeftRect(deviceWindowRect.translated(deviceWindowOffset), textureSize.height()); + const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect, + textureSize, + origin); + blitter->blit(textureId, QMatrix4x4(), source); + if (needsSwizzle) + blitter->setRedBlueSwizzle(false); + } + + // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set. + bool blendIsPremultiplied = premultiplied; + for (int i = 0; i < textures->count(); ++i) { + const QPlatformTextureList::Flags flags = textures->flags(i); + if (flags.testFlag(QPlatformTextureList::NeedsPremultipliedAlphaBlending)) { + if (!blendIsPremultiplied) { + funcs->glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + blendIsPremultiplied = true; + } + } else { + if (blendIsPremultiplied) { + funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + blendIsPremultiplied = false; + } + } + if (flags.testFlag(QPlatformTextureList::StacksOnTop)) + blitTextureForWidget(textures, i, window, deviceWindowRect, blitter, offset, canUseSrgb); + } + + funcs->glDisable(GL_BLEND); + blitter->release(); + + context->swapBuffers(window); +} + +GLuint QPlatformBackingStoreOpenGLSupport::toTexture(const QRegion &dirtyRegion, QSize *textureSize, QPlatformBackingStore::TextureFlags *flags) const +{ + Q_ASSERT(textureSize); + Q_ASSERT(flags); + + QImage image = backingStore->toImage(); + QSize imageSize = image.size(); + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + GLenum internalFormat = GL_RGBA; + GLuint pixelType = GL_UNSIGNED_BYTE; + + bool needsConversion = false; + *flags = { }; + switch (image.format()) { + case QImage::Format_ARGB32_Premultiplied: + *flags |= QPlatformBackingStore::TexturePremultiplied; + Q_FALLTHROUGH(); + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + *flags |= QPlatformBackingStore::TextureSwizzle; + break; + case QImage::Format_RGBA8888_Premultiplied: + *flags |= QPlatformBackingStore::TexturePremultiplied; + Q_FALLTHROUGH(); + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + break; + case QImage::Format_BGR30: + case QImage::Format_A2BGR30_Premultiplied: + if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + internalFormat = GL_RGB10_A2; + *flags |= QPlatformBackingStore::TexturePremultiplied; + } else { + needsConversion = true; + } + break; + case QImage::Format_RGB30: + case QImage::Format_A2RGB30_Premultiplied: + if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + internalFormat = GL_RGB10_A2; + *flags |= QPlatformBackingStore::TextureSwizzle | QPlatformBackingStore::TexturePremultiplied; + } else { + needsConversion = true; + } + break; + default: + needsConversion = true; + break; + } + if (imageSize.isEmpty()) { + *textureSize = imageSize; + return 0; + } + + // Must rely on the input only, not d_ptr. + // With the default composeAndFlush() textureSize is &d_ptr->textureSize. + bool resized = *textureSize != imageSize; + if (dirtyRegion.isEmpty() && !resized) + return textureId; + + *textureSize = imageSize; + + if (needsConversion) + image = image.convertToFormat(QImage::Format_RGBA8888); + + // The image provided by the backingstore may have a stride larger than width * 4, for + // instance on platforms that manually implement client-side decorations. + static const int bytesPerPixel = 4; + const int strideInPixels = image.bytesPerLine() / bytesPerPixel; + const bool hasUnpackRowLength = !ctx->isOpenGLES() || ctx->format().majorVersion() >= 3; + + QOpenGLFunctions *funcs = ctx->functions(); + + if (hasUnpackRowLength) { + funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, strideInPixels); + } else if (strideInPixels != image.width()) { + // No UNPACK_ROW_LENGTH on ES 2.0 and yet we would need it. This case is typically + // hit with QtWayland which is rarely used in combination with a ES2.0-only GL + // implementation. Therefore, accept the performance hit and do a copy. + image = image.copy(); + } + + if (resized) { + if (textureId) + funcs->glDeleteTextures(1, &textureId); + funcs->glGenTextures(1, &textureId); + funcs->glBindTexture(GL_TEXTURE_2D, textureId); + if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + } + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, imageSize.width(), imageSize.height(), 0, GL_RGBA, pixelType, + const_cast<uchar*>(image.constBits())); + } else { + funcs->glBindTexture(GL_TEXTURE_2D, textureId); + QRect imageRect = image.rect(); + QRect rect = dirtyRegion.boundingRect() & imageRect; + + if (hasUnpackRowLength) { + funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType, + image.constScanLine(rect.y()) + rect.x() * bytesPerPixel); + } else { + // if the rect is wide enough it's cheaper to just + // extend it instead of doing an image copy + if (rect.width() >= imageRect.width() / 2) { + rect.setX(0); + rect.setWidth(imageRect.width()); + } + + // if the sub-rect is full-width we can pass the image data directly to + // OpenGL instead of copying, since there's no gap between scanlines + + if (rect.width() == imageRect.width()) { + funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType, + image.constScanLine(rect.y())); + } else { + funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType, + image.copy(rect).constBits()); + } + } + } + + if (hasUnpackRowLength) + funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + return textureId; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE diff --git a/src/platformsupport/platformcompositor/qplatformbackingstoreopenglsupport.h b/src/platformsupport/platformcompositor/qplatformbackingstoreopenglsupport.h new file mode 100644 index 0000000000..5523b18f76 --- /dev/null +++ b/src/platformsupport/platformcompositor/qplatformbackingstoreopenglsupport.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMBACKINGSTOREOPENGLSUPPORT_H +#define QPLATFORMBACKINGSTOREOPENGLSUPPORT_H + +// +// W A R N I N G +// ------------- +// +// This file is part of the QPA API and is not meant to be used +// in applications. Usage of this API may make your code +// source and binary incompatible with future versions of Qt. +// + +#ifndef QT_NO_OPENGL + +#include <QtGui/qtguiglobal.h> +#include <qpa/qplatformbackingstore.h> + +#include <QtGui/QOpenGLContext> + +QT_BEGIN_NAMESPACE + +class QOpenGLTextureBlitter; +class QOpenGLBackingStore; + +class QPlatformBackingStoreOpenGLSupport : public QPlatformBackingStoreOpenGLSupportBase +{ +public: + explicit QPlatformBackingStoreOpenGLSupport(QPlatformBackingStore *backingStore) : backingStore(backingStore) {} + ~QPlatformBackingStoreOpenGLSupport() override; + void composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, + QPlatformTextureList *textures, bool translucentBackground) override; + GLuint toTexture(const QRegion &dirtyRegion, QSize *textureSize, QPlatformBackingStore::TextureFlags *flags) const override; + +private: + QPlatformBackingStore *backingStore = nullptr; + QScopedPointer<QOpenGLContext> context; + mutable GLuint textureId = 0; + mutable QSize textureSize; + mutable bool needsSwizzle = false; + mutable bool premultiplied = false; + QOpenGLTextureBlitter *blitter = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QPLATFORMBACKINGSTOREOPENGLSUPPORT_H diff --git a/src/plugins/platforms/android/android.pro b/src/plugins/platforms/android/android.pro index 61cac51633..8ea78f7cba 100644 --- a/src/plugins/platforms/android/android.pro +++ b/src/plugins/platforms/android/android.pro @@ -9,6 +9,8 @@ QT += \ qtConfig(vulkan): QT += vulkan_support-private +qtHaveModule(platformcompositor_support-private): QT += platformcompositor_support-private + OTHER_FILES += $$PWD/android.json INCLUDEPATH += \ diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index e0c437be27..ae584965e6 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -43,6 +43,9 @@ #include <QtGui/private/qguiapplication_p.h> #include <QGuiApplication> #include <QOpenGLContext> +#if QT_CONFIG(opengl) +#include <QtPlatformCompositorSupport/qpa/qplatformbackingstoreopenglsupport.h> +#endif #include <QThread> #include <QOffscreenSurface> @@ -275,7 +278,12 @@ QPlatformBackingStore *QAndroidPlatformIntegration::createPlatformBackingStore(Q { if (!QtAndroid::activity()) return nullptr; - return new QAndroidPlatformBackingStore(window); + + auto *backingStore = new QAndroidPlatformBackingStore(window); +#if QT_CONFIG(opengl) + backingStore->setOpenGLSupport(new QPlatformBackingStoreOpenGLSupport(backingStore)); +#endif // QT_CONFIG(opengl) + return backingStore; } QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index a919963cf4..953346c56e 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -101,6 +101,8 @@ QT += \ qtConfig(vulkan): QT += vulkan_support-private +qtHaveModule(platformcompositor_support-private): QT += platformcompositor_support-private + CONFIG += no_app_extension_api_only qtHaveModule(widgets) { diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index a77b97f538..b2698b05fe 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -68,6 +68,10 @@ #include <QtFontDatabaseSupport/private/qfontengine_coretext_p.h> +#if QT_CONFIG(opengl) +#include <QtPlatformCompositorSupport/qpa/qplatformbackingstoreopenglsupport.h> +#endif + #ifdef QT_WIDGETS_LIB #include <QtWidgets/qtwidgetsglobal.h> #if QT_CONFIG(filedialog) @@ -324,10 +328,16 @@ QPlatformBackingStore *QCocoaIntegration::createPlatformBackingStore(QWindow *wi return nullptr; } + QPlatformBackingStore *backingStore = nullptr; if (platformWindow->view().layer) - return new QCALayerBackingStore(window); + backingStore = new QCALayerBackingStore(window); else - return new QNSWindowBackingStore(window); + backingStore = new QNSWindowBackingStore(window); + +#if QT_CONFIG(opengl) + backingStore->setOpenGLSupport(new QPlatformBackingStoreOpenGLSupport(backingStore)); +#endif + return backingStore; } QAbstractEventDispatcher *QCocoaIntegration::createEventDispatcher() const diff --git a/src/plugins/platforms/ios/kernel.pro b/src/plugins/platforms/ios/kernel.pro index 71257d09f7..01e0105223 100644 --- a/src/plugins/platforms/ios/kernel.pro +++ b/src/plugins/platforms/ios/kernel.pro @@ -9,6 +9,8 @@ QT += \ core-private gui-private \ clipboard_support-private fontdatabase_support-private graphics_support-private +qtHaveModule(platformcompositor_support-private): QT += platformcompositor_support-private + LIBS += -framework Foundation -framework UIKit -framework QuartzCore -framework AudioToolbox OBJECTIVE_SOURCES = \ diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 9eca0eaad3..d724e65717 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -65,6 +65,10 @@ #import <AudioToolbox/AudioServices.h> +#if QT_CONFIG(opengl) +#include <QtPlatformCompositorSupport/qpa/qplatformbackingstoreopenglsupport.h> +#endif + #include <QtDebug> QT_BEGIN_NAMESPACE @@ -186,7 +190,11 @@ QPlatformWindow *QIOSIntegration::createPlatformWindow(QWindow *window) const // Used when the QWindow's surface type is set by the client to QSurface::RasterSurface QPlatformBackingStore *QIOSIntegration::createPlatformBackingStore(QWindow *window) const { - return new QIOSBackingStore(window); + auto *backingStore = new QIOSBackingStore(window); +#if QT_CONFIG(opengl) + backingStore->setOpenGLSupport(new QPlatformBackingStoreOpenGLSupport(backingStore)); +#endif + return backingStore; } // Used when the QWindow's surface type is set by the client to QSurface::OpenGLSurface diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index 6bf0d69770..74890ead82 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -36,7 +36,6 @@ #include <QtGui/private/qwindow_p.h> #include <QtGui/qopenglcontext.h> #include <QtGui/qopenglfunctions.h> -#include <QtGui/qopengltextureblitter.h> #include <QtGui/qoffscreensurface.h> #include <QtGui/qpainter.h> #include <private/qpixmapcache_p.h> diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index 6b59d87a0a..250d244c9f 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -33,7 +33,7 @@ #include <QtGui/qregion.h> #include <qpa/qplatformwindow.h> -#include <QtGui/qopengltextureblitter.h> +#include <QtOpenGL/qopengltextureblitter.h> #include <QtGui/qpalette.h> #include <QtGui/qpainter.h> @@ -43,7 +43,6 @@ class QWasmWindow; class QWasmScreen; class QOpenGLContext; class QOpenGLTexture; -class QOpenGLTextureBlitter; class QWasmCompositedWindow { diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index fd53cd0bae..ce83ad4e2f 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -41,6 +41,7 @@ #include "qwasmwindow.h" #ifndef QT_NO_OPENGL # include "qwasmbackingstore.h" +# include <QtPlatformCompositorSupport/qpa/qplatformbackingstoreopenglsupport.h> #endif #include "qwasmfontdatabase.h" #if defined(Q_OS_UNIX) @@ -185,6 +186,7 @@ QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *win #ifndef QT_NO_OPENGL QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor(); QWasmBackingStore *backingStore = new QWasmBackingStore(compositor, window); + backingStore->setOpenGLSupport(new QPlatformBackingStoreOpenGLSupport(backingStore)); m_backingStores.insert(window, backingStore); return backingStore; #else diff --git a/src/plugins/platforms/wasm/wasm.pro b/src/plugins/platforms/wasm/wasm.pro index c8b28fb37d..1aee4a3e58 100644 --- a/src/plugins/platforms/wasm/wasm.pro +++ b/src/plugins/platforms/wasm/wasm.pro @@ -4,6 +4,8 @@ QT += \ core-private gui-private \ eventdispatcher_support-private fontdatabase_support-private egl_support-private +qtHaveModule(platformcompositor_support-private): QT += platformcompositor_support-private + # Avoid X11 header collision, use generic EGL native types DEFINES += QT_EGL_NO_X11 diff --git a/src/plugins/platforms/windows/qwindowsgdiintegration.cpp b/src/plugins/platforms/windows/qwindowsgdiintegration.cpp index c88f669eb5..7e9595321a 100644 --- a/src/plugins/platforms/windows/qwindowsgdiintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsgdiintegration.cpp @@ -45,6 +45,10 @@ #include <QtCore/qdebug.h> #include <QtGui/private/qpixmap_raster_p.h> +#if QT_CONFIG(opengl) +#include <QtPlatformCompositorSupport/qpa/qplatformbackingstoreopenglsupport.h> +#endif + QT_BEGIN_NAMESPACE class QWindowsGdiIntegrationPrivate @@ -73,7 +77,11 @@ QPlatformPixmap *QWindowsGdiIntegration::createPlatformPixmap(QPlatformPixmap::P QPlatformBackingStore *QWindowsGdiIntegration::createPlatformBackingStore(QWindow *window) const { - return new QWindowsBackingStore(window); + auto *backingStore = new QWindowsBackingStore(window); +#ifndef QT_NO_OPENGL + backingStore->setOpenGLSupport(new QPlatformBackingStoreOpenGLSupport(backingStore)); +#endif + return backingStore; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 4b4047ac0c..77340387d8 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -48,6 +48,9 @@ #include "qwindowsscreen.h" #include "qwindowstheme.h" #include "qwindowsservices.h" +#ifdef QT_USE_DIRECTWRITE3 +#include <QtFontDatabaseSupport/private/qwindowsdirectwritefontdatabase_p.h> +#endif #ifndef QT_NO_FREETYPE # include <QtFontDatabaseSupport/private/qwindowsfontdatabase_ft_p.h> #endif @@ -187,7 +190,9 @@ static inline unsigned parseOptions(const QStringList ¶mList, unsigned options = 0; for (const QString ¶m : paramList) { if (param.startsWith(u"fontengine=")) { - if (param.endsWith(u"freetype")) { + if (param.endsWith(u"directwrite")) { + options |= QWindowsIntegration::FontDatabaseDirectWrite; + } else if (param.endsWith(u"freetype")) { options |= QWindowsIntegration::FontDatabaseFreeType; } else if (param.endsWith(u"native")) { options |= QWindowsIntegration::FontDatabaseNative; @@ -504,14 +509,17 @@ QWindowsStaticOpenGLContext *QWindowsIntegration::staticOpenGLContext() QPlatformFontDatabase *QWindowsIntegration::fontDatabase() const { if (!d->m_fontDatabase) { -#ifdef QT_NO_FREETYPE - d->m_fontDatabase = new QWindowsFontDatabase(); -#else // QT_NO_FREETYPE +#ifdef QT_USE_DIRECTWRITE3 + if (d->m_options & QWindowsIntegration::FontDatabaseDirectWrite) + d->m_fontDatabase = new QWindowsDirectWriteFontDatabase; + else +#endif +#ifndef QT_NO_FREETYPE if (d->m_options & QWindowsIntegration::FontDatabaseFreeType) d->m_fontDatabase = new QWindowsFontDatabaseFT; else - d->m_fontDatabase = new QWindowsFontDatabase; #endif // QT_NO_FREETYPE + d->m_fontDatabase = new QWindowsFontDatabase(); } return d->m_fontDatabase; } diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index 1f16d13769..165472ad40 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -72,7 +72,8 @@ public: DetectAltGrModifier = 0x800, RtlEnabled = 0x1000, DarkModeWindowFrames = 0x2000, - DarkModeStyle = 0x4000 + DarkModeStyle = 0x4000, + FontDatabaseDirectWrite = 0x8000 }; explicit QWindowsIntegration(const QStringList ¶mList); diff --git a/src/plugins/platforms/windows/windows.pro b/src/plugins/platforms/windows/windows.pro index 50a3bb41a9..8a27bd2770 100644 --- a/src/plugins/platforms/windows/windows.pro +++ b/src/plugins/platforms/windows/windows.pro @@ -5,6 +5,8 @@ QT += \ eventdispatcher_support-private \ fontdatabase_support-private theme_support-private +qtHaveModule(platformcompositor_support-private): QT += platformcompositor_support-private + qtConfig(accessibility): QT += accessibility_support-private qtConfig(vulkan): QT += vulkan_support-private diff --git a/src/plugins/platforms/winrt/qwinrtintegration.cpp b/src/plugins/platforms/winrt/qwinrtintegration.cpp index 27d3746933..dd8cd80fd9 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.cpp +++ b/src/plugins/platforms/winrt/qwinrtintegration.cpp @@ -53,6 +53,9 @@ #if QT_CONFIG(accessibility) # include "uiautomation/qwinrtuiaaccessibility.h" #endif +#if QT_CONFIG(opengl) +#include <QtPlatformCompositorSupport/qpa/qplatformbackingstoreopenglsupport.h> +#endif #include <QtGui/QOffscreenSurface> #include <QtGui/QOpenGLContext> @@ -205,7 +208,11 @@ QPlatformWindow *QWinRTIntegration::createPlatformWindow(QWindow *window) const QPlatformBackingStore *QWinRTIntegration::createPlatformBackingStore(QWindow *window) const { - return new QWinRTBackingStore(window); + auto *backingStore = new QWinRTBackingStore(window); +#if QT_CONFIG(opengl) + backingStore->setOpenGLSupport(new QPlatformBackingStoreOpenGLSupport(backingStore)); +#endif + return backingStore; } QPlatformOpenGLContext *QWinRTIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro index 43dc8f074c..7ac49f73c4 100644 --- a/src/plugins/platforms/winrt/winrt.pro +++ b/src/plugins/platforms/winrt/winrt.pro @@ -6,6 +6,8 @@ QT += \ core-private gui-private \ fontdatabase_support-private egl_support-private +qtHaveModule(platformcompositor_support-private): QT += platformcompositor_support-private + DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ QMAKE_USE_PRIVATE += d3d11 ws2_32 diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 3fd989e1f9..cea0511822 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -61,6 +61,9 @@ #include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h> #include <QtServiceSupport/private/qgenericunixservices_p.h> +#if QT_CONFIG(opengl) +#include <QtPlatformCompositorSupport/qpa/qplatformbackingstoreopenglsupport.h> +#endif #include <stdio.h> @@ -288,16 +291,23 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *window) const { - const bool isTrayIconWindow = QXcbWindow::isTrayIconWindow(window); - if (isTrayIconWindow) - return new QXcbSystemTrayBackingStore(window); + QPlatformBackingStore *backingStore = nullptr; + const bool isTrayIconWindow = QXcbWindow::isTrayIconWindow(window); + if (isTrayIconWindow) { + backingStore = new QXcbSystemTrayBackingStore(window); #if QT_CONFIG(xcb_native_painting) - if (nativePaintingEnabled()) - return new QXcbNativeBackingStore(window); + } else if (nativePaintingEnabled()) { + backingStore = new QXcbNativeBackingStore(window); #endif - - return new QXcbBackingStore(window); + } else { + backingStore = new QXcbBackingStore(window); + } + Q_ASSERT(backingStore); +#ifndef QT_NO_OPENGL + backingStore->setOpenGLSupport(new QPlatformBackingStoreOpenGLSupport(backingStore)); +#endif + return backingStore; } QPlatformOffscreenSurface *QXcbIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro index a5d05faa9c..1f651e7697 100644 --- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro +++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro @@ -9,6 +9,9 @@ QT += \ edid_support-private \ xkbcommon_support-private +qtHaveModule(platformcompositor_support-private): \ + QT += platformcompositor_support-private + qtHaveModule(linuxaccessibility_support-private): \ QT += linuxaccessibility_support-private diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index f41b0e8ba4..93bc4d8c4e 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -476,7 +476,7 @@ void Generator::generateCode() QByteArray unqualifiedScope = p.type.left(s); // The scope may be a namespace for example, so it's only safe to include scopes that are known QObjects (QTBUG-2151) - QHash<QByteArray, QByteArray>::ConstIterator scopeIt; + QMultiHash<QByteArray, QByteArray>::ConstIterator scopeIt; QByteArray thisScope = cdef->qualified; do { diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp index 7188c81300..5bd1fba272 100644 --- a/src/tools/rcc/rcc.cpp +++ b/src/tools/rcc/rcc.cpp @@ -744,10 +744,9 @@ bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file) const QString filename = nodes.at(nodes.size()-1); RCCFileInfo *s = new RCCFileInfo(file); s->m_parent = parent; - typedef QHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator; - const ChildConstIterator cbegin = parent->m_children.constFind(filename); - const ChildConstIterator cend = parent->m_children.constEnd(); - for (ChildConstIterator it = cbegin; it != cend; ++it) { + auto cbegin = parent->m_children.constFind(filename); + auto cend = parent->m_children.constEnd(); + for (auto it = cbegin; it != cend; ++it) { if (it.key() == filename && it.value()->m_language == s->m_language && it.value()->m_country == s->m_country) { for (const QString &name : qAsConst(m_fileNames)) { @@ -823,7 +822,7 @@ QStringList RCCResourceLibrary::dataFiles() const pending.push(m_root); while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); - for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); + for (auto it = file->m_children.begin(); it != file->m_children.end(); ++it) { RCCFileInfo *child = it.value(); if (child->m_flags & RCCFileInfo::Directory) @@ -838,10 +837,9 @@ QStringList RCCResourceLibrary::dataFiles() const // Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m) { - typedef QHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator; const QChar slash = QLatin1Char('/'); - const ChildConstIterator cend = m_root->m_children.constEnd(); - for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) { + const auto cend = m_root->m_children.constEnd(); + for (auto it = m_root->m_children.constBegin(); it != cend; ++it) { const RCCFileInfo *child = it.value(); const QString childName = path + slash + child->m_name; if (child->m_flags & RCCFileInfo::Directory) { @@ -1149,8 +1147,7 @@ bool RCCResourceLibrary::writeDataBlobs() QString errorMessage; while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); - for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); - it != file->m_children.end(); ++it) { + for (auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) { RCCFileInfo *child = it.value(); if (child->m_flags & RCCFileInfo::Directory) pending.push(child); @@ -1214,8 +1211,7 @@ bool RCCResourceLibrary::writeDataNames() qint64 offset = 0; while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); - for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); - it != file->m_children.end(); ++it) { + for (auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) { RCCFileInfo *child = it.value(); if (child->m_flags & RCCFileInfo::Directory) pending.push(child); diff --git a/src/widgets/widgets/qabstractbutton.cpp b/src/widgets/widgets/qabstractbutton.cpp index 7c26b8cc42..77ffda10b0 100644 --- a/src/widgets/widgets/qabstractbutton.cpp +++ b/src/widgets/widgets/qabstractbutton.cpp @@ -413,16 +413,8 @@ void QAbstractButtonPrivate::emitClicked() QPointer<QAbstractButton> guard(q); emit q->clicked(checked); #if QT_CONFIG(buttongroup) - if (guard && group) { -#if QT_DEPRECATED_SINCE(5, 15) -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - emit group->buttonClicked(group->id(q)); - if (guard && group) -QT_WARNING_POP -#endif - emit group->buttonClicked(q); - } + if (guard && group) + emit group->buttonClicked(q); #endif } @@ -432,16 +424,8 @@ void QAbstractButtonPrivate::emitPressed() QPointer<QAbstractButton> guard(q); emit q->pressed(); #if QT_CONFIG(buttongroup) - if (guard && group) { -#if QT_DEPRECATED_SINCE(5, 15) -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - emit group->buttonPressed(group->id(q)); - if (guard && group) -QT_WARNING_POP -#endif - emit group->buttonPressed(q); - } + if (guard && group) + emit group->buttonPressed(q); #endif } @@ -451,16 +435,8 @@ void QAbstractButtonPrivate::emitReleased() QPointer<QAbstractButton> guard(q); emit q->released(); #if QT_CONFIG(buttongroup) - if (guard && group) { -#if QT_DEPRECATED_SINCE(5, 15) -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - emit group->buttonReleased(group->id(q)); - if (guard && group) -QT_WARNING_POP -#endif - emit group->buttonReleased(q); - } + if (guard && group) + emit group->buttonReleased(q); #endif } @@ -470,16 +446,8 @@ void QAbstractButtonPrivate::emitToggled(bool checked) QPointer<QAbstractButton> guard(q); emit q->toggled(checked); #if QT_CONFIG(buttongroup) - if (guard && group) { -#if QT_DEPRECATED_SINCE(5, 15) -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - emit group->buttonToggled(group->id(q), checked); - if (guard && group) -QT_WARNING_POP -#endif - emit group->buttonToggled(q, checked); - } + if (guard && group) + emit group->buttonToggled(q, checked); #endif } diff --git a/src/widgets/widgets/qbuttongroup.cpp b/src/widgets/widgets/qbuttongroup.cpp index 9162029cdb..1368c721ad 100644 --- a/src/widgets/widgets/qbuttongroup.cpp +++ b/src/widgets/widgets/qbuttongroup.cpp @@ -163,16 +163,6 @@ void QButtonGroup::setExclusive(bool exclusive) */ /*! - \fn void QButtonGroup::buttonClicked(int id) - \obsolete - - This signal is emitted when a button with the given \a id is - clicked. - - \sa checkedButton(), QAbstractButton::clicked() -*/ - -/*! \fn void QButtonGroup::buttonPressed(QAbstractButton *button) \since 4.2 @@ -182,17 +172,6 @@ void QButtonGroup::setExclusive(bool exclusive) */ /*! - \fn void QButtonGroup::buttonPressed(int id) - \since 4.2 - \obsolete - - This signal is emitted when a button with the given \a id is - pressed down. - - \sa QAbstractButton::pressed() -*/ - -/*! \fn void QButtonGroup::buttonReleased(QAbstractButton *button) \since 4.2 @@ -202,17 +181,6 @@ void QButtonGroup::setExclusive(bool exclusive) */ /*! - \fn void QButtonGroup::buttonReleased(int id) - \since 4.2 - \obsolete - - This signal is emitted when a button with the given \a id is - released. - - \sa QAbstractButton::released() -*/ - -/*! \fn void QButtonGroup::buttonToggled(QAbstractButton *button, bool checked) \since 5.2 @@ -222,17 +190,6 @@ void QButtonGroup::setExclusive(bool exclusive) \sa QAbstractButton::toggled() */ -/*! - \fn void QButtonGroup::buttonToggled(int id, bool checked) - \since 5.2 - \obsolete - - This signal is emitted when a button with the given \a id is toggled. - \a checked is true if the button is checked, or false if the button is unchecked. - - \sa QAbstractButton::toggled() -*/ - /*! Adds the given \a button to the button group. If \a id is -1, diff --git a/src/widgets/widgets/qbuttongroup.h b/src/widgets/widgets/qbuttongroup.h index 2989dcb4ba..838bf8abce 100644 --- a/src/widgets/widgets/qbuttongroup.h +++ b/src/widgets/widgets/qbuttongroup.h @@ -81,16 +81,6 @@ Q_SIGNALS: void buttonPressed(QAbstractButton *); void buttonReleased(QAbstractButton *); void buttonToggled(QAbstractButton *, bool); -#if QT_DEPRECATED_SINCE(5, 15) - QT_DEPRECATED_VERSION_X_5_15("Use QButtonGroup::buttonClicked(QAbstractButton *) instead") - void buttonClicked(int); - QT_DEPRECATED_VERSION_X_5_15("Use QButtonGroup::buttonPressed(QAbstractButton *) instead") - void buttonPressed(int); - QT_DEPRECATED_VERSION_X_5_15("Use QButtonGroup::buttonReleased(QAbstractButton *) instead") - void buttonReleased(int); - QT_DEPRECATED_VERSION_X_5_15("Use QButtonGroup::buttonToggled(QAbstractButton *, bool) instead") - void buttonToggled(int, bool); -#endif private: Q_DISABLE_COPY(QButtonGroup) diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index 3da7e365c8..f8bccfbfa3 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -2512,7 +2512,7 @@ void QWidgetTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelectio emit updateRequest(r); } - for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) { + for (auto it = hash.cbegin(); it != hash.cend(); ++it) { const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value()); QRectF r = selectionRect(esel.cursor); if (esel.format.boolProperty(QTextFormat::FullWidthSelection)) { diff --git a/src/xml/dom/qdom.cpp b/src/xml/dom/qdom.cpp index 647e22bf26..1ac13b380a 100644 --- a/src/xml/dom/qdom.cpp +++ b/src/xml/dom/qdom.cpp @@ -2550,7 +2550,7 @@ QDomNamedNodeMapPrivate* QDomNamedNodeMapPrivate::clone(QDomNodePrivate* p) m->readonly = readonly; m->appendToParent = appendToParent; - QHash<QString, QDomNodePrivate*>::const_iterator it = map.constBegin(); + auto it = map.constBegin(); for (; it != map.constEnd(); ++it) { QDomNodePrivate *new_node = (*it)->cloneNode(); new_node->setParent(p); @@ -2566,7 +2566,7 @@ void QDomNamedNodeMapPrivate::clearMap() { // Dereference all of our children if we took references if (!appendToParent) { - QHash<QString, QDomNodePrivate *>::const_iterator it = map.constBegin(); + auto it = map.constBegin(); for (; it != map.constEnd(); ++it) if (!(*it)->ref.deref()) delete *it; @@ -2582,7 +2582,7 @@ QDomNodePrivate* QDomNamedNodeMapPrivate::namedItem(const QString& name) const QDomNodePrivate* QDomNamedNodeMapPrivate::namedItemNS(const QString& nsURI, const QString& localName) const { - QHash<QString, QDomNodePrivate *>::const_iterator it = map.constBegin(); + auto it = map.constBegin(); QDomNodePrivate *n; for (; it != map.constEnd(); ++it) { n = *it; @@ -3098,11 +3098,11 @@ void QDomDocumentTypePrivate::save(QTextStream& s, int, int indent) const if (entities->length()>0 || notations->length()>0) { s << " [" << Qt::endl; - QHash<QString, QDomNodePrivate *>::const_iterator it2 = notations->map.constBegin(); + auto it2 = notations->map.constBegin(); for (; it2 != notations->map.constEnd(); ++it2) (*it2)->save(s, 0, indent); - QHash<QString, QDomNodePrivate *>::const_iterator it = entities->map.constBegin(); + auto it = entities->map.constBegin(); for (; it != entities->map.constEnd(); ++it) (*it)->save(s, 0, indent); @@ -4088,7 +4088,7 @@ void QDomElementPrivate::save(QTextStream& s, int depth, int indent) const /* Write out attributes. */ if (!m_attr->map.isEmpty()) { QDuplicateTracker<QString> outputtedPrefixes; - QHash<QString, QDomNodePrivate *>::const_iterator it = m_attr->map.constBegin(); + auto it = m_attr->map.constBegin(); for (; it != m_attr->map.constEnd(); ++it) { s << ' '; if (it.value()->namespaceURI.isNull()) { diff --git a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp index 877de3f95d..d1a77173c3 100644 --- a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp +++ b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp @@ -121,6 +121,9 @@ private slots: void stream_Map_data(); void stream_Map(); + void stream_MultiMap_data(); + void stream_MultiMap(); + void stream_Hash_data(); void stream_Hash(); @@ -233,6 +236,7 @@ private: void writeQRegularExpression(QDataStream *dev); #endif void writeMap(QDataStream* dev); + void writeMultiMap(QDataStream* dev); void writeHash(QDataStream* dev); void writeMultiHash(QDataStream* dev); void writeqint64(QDataStream *s); @@ -266,6 +270,7 @@ private: void readQRegularExpression(QDataStream *s); #endif void readMap(QDataStream *s); + void readMultiMap(QDataStream *s); void readHash(QDataStream *s); void readMultiHash(QDataStream *s); void readqint64(QDataStream *s); @@ -655,16 +660,10 @@ static Map MapData(int index) map.insert(2, "bbb"); map.insert(3, "cccccc"); break; - case 2: - map.insert(1, "a"); - map.insert(2, "one"); - map.insertMulti(2, "two"); - map.insertMulti(2, "three"); - map.insert(3, "cccccc"); } return map; } -#define MAX_MAP_DATA 3 +#define MAX_MAP_DATA 2 void tst_QDataStream::stream_Map_data() { @@ -694,6 +693,60 @@ void tst_QDataStream::readMap(QDataStream *s) QCOMPARE(S, test); } +typedef QMultiMap<int, QString> MultiMap; + +static MultiMap MultiMapData(int index) +{ + MultiMap map; + + switch (index) { + case 0: + default: + break; + case 1: + map.insert(1, "a"); + map.insert(2, "bbb"); + map.insert(3, "cccccc"); + break; + case 2: + map.insert(1, "a"); + map.insert(2, "one"); + map.insert(2, "two"); + map.insert(2, "three"); + map.insert(3, "cccccc"); + } + return map; +} +#define MAX_MULTIMAP_DATA 3 + +void tst_QDataStream::stream_MultiMap_data() +{ + stream_data(MAX_MULTIMAP_DATA); +} + +void tst_QDataStream::stream_MultiMap() +{ + STREAM_IMPL(MultiMap); +} + +void tst_QDataStream::writeMultiMap(QDataStream* s) +{ + MultiMap test(MultiMapData(dataIndex(QTest::currentDataTag()))); + *s << test; + *s << test; +} + +void tst_QDataStream::readMultiMap(QDataStream *s) +{ + MultiMap S; + MultiMap test(MultiMapData(dataIndex(QTest::currentDataTag()))); + + *s >> S; + QCOMPARE(S, test); + *s >> S; + QCOMPARE(S, test); +} + // ************************************ typedef QHash<int, QString> Hash; diff --git a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp index a9fd282ac9..8a621cd5d9 100644 --- a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp @@ -2155,7 +2155,7 @@ void tst_QSharedPointer::threadStressTest() } } -template<typename Container, bool Ordered> +template<typename Container, bool Ordered, bool Multi> void hashAndMapTest() { typedef typename Container::key_type Key; @@ -2200,26 +2200,30 @@ void hashAndMapTest() QVERIFY(it == c.end()); } - c.insertMulti(k1, Value(47)); - it = c.find(k1); - QVERIFY(it != c.end()); - QCOMPARE(it.key(), k1); - ++it; - QVERIFY(it != c.end()); - QCOMPARE(it.key(), k1); - ++it; - if (Ordered) - QVERIFY(it == c.end()); + if (Multi) { + c.insert(k1, Value(47)); + it = c.find(k1); + QVERIFY(it != c.end()); + QCOMPARE(it.key(), k1); + ++it; + QVERIFY(it != c.end()); + QCOMPARE(it.key(), k1); + ++it; + if (Ordered) + QVERIFY(it == c.end()); + } } void tst_QSharedPointer::map() { - hashAndMapTest<QMap<QSharedPointer<int>, int>, true>(); + hashAndMapTest<QMap<QSharedPointer<int>, int>, true, false>(); + hashAndMapTest<QMultiMap<QSharedPointer<int>, int>, true, true>(); } void tst_QSharedPointer::hash() { - hashAndMapTest<QHash<QSharedPointer<int>, int>, false>(); + hashAndMapTest<QHash<QSharedPointer<int>, int>, false, false>(); + hashAndMapTest<QMultiHash<QSharedPointer<int>, int>, false, true>(); } void tst_QSharedPointer::validConstructs() diff --git a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp index 63a6618dab..928bd365fd 100644 --- a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp +++ b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp @@ -81,9 +81,10 @@ private slots: void serialize(); void moveSemantics(); void qtVersion(); - void qPropertyRevision_data(); - void qPropertyRevision(); - void qPropertyRevisionTypes(); + void qTypeRevision_data(); + void qTypeRevision(); + void qTypeRevisionTypes(); + void qTypeRevisionComparison(); }; void tst_QVersionNumber::singleInstanceData() @@ -704,7 +705,7 @@ void compileTestRevision<qint8>() compileTestRevisionMajorMinor<qint8>(); } -void tst_QVersionNumber::qPropertyRevision_data() +void tst_QVersionNumber::qTypeRevision_data() { QTest::addColumn<QTypeRevision>("revision"); QTest::addColumn<bool>("valid"); @@ -723,7 +724,7 @@ void tst_QVersionNumber::qPropertyRevision_data() // You must not pass them as major or minor versions, or values. } -void tst_QVersionNumber::qPropertyRevision() +void tst_QVersionNumber::qTypeRevision() { const QTypeRevision other = QTypeRevision::fromVersion(127, 128); @@ -747,7 +748,7 @@ void tst_QVersionNumber::qPropertyRevision() QVERIFY(copy != other); } -void tst_QVersionNumber::qPropertyRevisionTypes() +void tst_QVersionNumber::qTypeRevisionTypes() { compileTestRevision<quint64>(); compileTestRevision<qint64>(); @@ -760,6 +761,41 @@ void tst_QVersionNumber::qPropertyRevisionTypes() QVERIFY(maxRevision.hasMinorVersion()); } +void tst_QVersionNumber::qTypeRevisionComparison() +{ + const QTypeRevision revisions[] = { + QTypeRevision::zero(), + QTypeRevision::fromMajorVersion(0), + QTypeRevision::fromVersion(0, 1), + QTypeRevision::fromVersion(0, 20), + QTypeRevision::fromMinorVersion(0), + QTypeRevision(), + QTypeRevision::fromMinorVersion(1), + QTypeRevision::fromMinorVersion(20), + QTypeRevision::fromVersion(1, 0), + QTypeRevision::fromMajorVersion(1), + QTypeRevision::fromVersion(1, 1), + QTypeRevision::fromVersion(1, 20), + QTypeRevision::fromVersion(20, 0), + QTypeRevision::fromMajorVersion(20), + QTypeRevision::fromVersion(20, 1), + QTypeRevision::fromVersion(20, 20), + }; + + const int length = sizeof(revisions) / sizeof(QTypeRevision); + + for (int i = 0; i < length; ++i) { + for (int j = 0; j < length; ++j) { + QCOMPARE(revisions[i] == revisions[j], i == j); + QCOMPARE(revisions[i] != revisions[j], i != j); + QCOMPARE(revisions[i] < revisions[j], i < j); + QCOMPARE(revisions[i] > revisions[j], i > j); + QCOMPARE(revisions[i] <= revisions[j], i <= j); + QCOMPARE(revisions[i] >= revisions[j], i >= j); + } + } +} + QTEST_APPLESS_MAIN(tst_QVersionNumber) #include "tst_qversionnumber.moc" diff --git a/tests/auto/gui/qopengl/tst_qopengl.cpp b/tests/auto/gui/qopengl/tst_qopengl.cpp index 3a7ad6dad5..cf839e1931 100644 --- a/tests/auto/gui/qopengl/tst_qopengl.cpp +++ b/tests/auto/gui/qopengl/tst_qopengl.cpp @@ -28,6 +28,7 @@ #include <QtOpenGL/QOpenGLPaintDevice> #include <QtOpenGL/QOpenGLTexture> +#include <QtOpenGL/qopengltextureblitter.h> #include <QtGui/private/qopenglcontext_p.h> #include <QtGui/QOpenGLFramebufferObject> #include <QtGui/QOpenGLFunctions> @@ -40,7 +41,6 @@ #include <QtGui/QOffscreenSurface> #include <QtGui/QGenericMatrix> #include <QtGui/QMatrix4x4> -#include <QtGui/qopengltextureblitter.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qopenglextensions_p.h> #include <qpa/qplatformintegration.h> diff --git a/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp index 5be00630ca..1ef9382f0a 100644 --- a/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp +++ b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp @@ -530,11 +530,17 @@ void tst_PlatformSocketEngine::tooManySockets() void tst_PlatformSocketEngine::bind() { #if !defined Q_OS_WIN - PLATFORMSOCKETENGINE binder; - QVERIFY(binder.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); - QVERIFY(!binder.bind(QHostAddress::AnyIPv4, 82)); - QCOMPARE(binder.error(), QAbstractSocket::SocketAccessError); -#endif +#if defined Q_OS_MACOS + // On macOS >= 10.14 the bind on this port is successful. + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSMojave) +#endif // Q_OS_MACOS + { + PLATFORMSOCKETENGINE binder; + QVERIFY(binder.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(!binder.bind(QHostAddress::AnyIPv4, 82)); + QCOMPARE(binder.error(), QAbstractSocket::SocketAccessError); + } +#endif // Q_OS_WIN PLATFORMSOCKETENGINE binder2; QVERIFY(binder2.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); diff --git a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp index 9ab5e88900..bf08d70eb3 100644 --- a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp +++ b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp @@ -558,8 +558,18 @@ void tst_QTcpSocket::bind_data() // try to bind to a privileged ports // we should fail if we're not root (unless the ports are in use!) QTest::newRow("127.0.0.1:1") << "127.0.0.1" << 1 << !geteuid() << (geteuid() ? QString() : "127.0.0.1"); - if (testIpv6) + if (testIpv6) { +#ifdef Q_OS_DARWIN + // This case is faling in different ways, first, it manages to bind to + // port 1 on macOS >= 10.14, but if we change the logic to not fail, + // it's becoming flaky and sometimes fails to bind, with error 'port in use' + // (apparently inflicted by the previous test row with 127.0.0.1). Amen. + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) + QTest::qWarn("Skipping [::]:1, see QTBUG-81905", __FILE__, __LINE__); + else +#endif // Q_OS_DARWIN QTest::newRow("[::]:1") << "::" << 1 << !geteuid() << (geteuid() ? QString() : "::"); + } #endif } diff --git a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp index 47dfc86a69..5d52acd1a2 100644 --- a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp +++ b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp @@ -280,67 +280,43 @@ void tst_QButtonGroup::testSignals() qRegisterMetaType<QAbstractButton *>("QAbstractButton *"); QSignalSpy clickedSpy(&buttons, SIGNAL(buttonClicked(QAbstractButton*))); - QSignalSpy clickedIdSpy(&buttons, SIGNAL(buttonClicked(int))); QSignalSpy pressedSpy(&buttons, SIGNAL(buttonPressed(QAbstractButton*))); - QSignalSpy pressedIdSpy(&buttons, SIGNAL(buttonPressed(int))); QSignalSpy releasedSpy(&buttons, SIGNAL(buttonReleased(QAbstractButton*))); - QSignalSpy releasedIdSpy(&buttons, SIGNAL(buttonReleased(int))); pb1.animateClick(); QTestEventLoop::instance().enterLoop(1); QCOMPARE(clickedSpy.count(), 1); - QCOMPARE(clickedIdSpy.count(), 1); - int expectedId = -2; - - QCOMPARE(clickedIdSpy.takeFirst().at(0).toInt(), expectedId); QCOMPARE(pressedSpy.count(), 1); - QCOMPARE(pressedIdSpy.count(), 1); - QCOMPARE(pressedIdSpy.takeFirst().at(0).toInt(), expectedId); QCOMPARE(releasedSpy.count(), 1); - QCOMPARE(releasedIdSpy.count(), 1); - QCOMPARE(releasedIdSpy.takeFirst().at(0).toInt(), expectedId); clickedSpy.clear(); - clickedIdSpy.clear(); pressedSpy.clear(); - pressedIdSpy.clear(); releasedSpy.clear(); - releasedIdSpy.clear(); pb2.animateClick(); QTestEventLoop::instance().enterLoop(1); QCOMPARE(clickedSpy.count(), 1); - QCOMPARE(clickedIdSpy.count(), 1); - QCOMPARE(clickedIdSpy.takeFirst().at(0).toInt(), 23); QCOMPARE(pressedSpy.count(), 1); - QCOMPARE(pressedIdSpy.count(), 1); - QCOMPARE(pressedIdSpy.takeFirst().at(0).toInt(), 23); QCOMPARE(releasedSpy.count(), 1); - QCOMPARE(releasedIdSpy.count(), 1); - QCOMPARE(releasedIdSpy.takeFirst().at(0).toInt(), 23); QSignalSpy toggledSpy(&buttons, SIGNAL(buttonToggled(QAbstractButton*, bool))); - QSignalSpy toggledIdSpy(&buttons, SIGNAL(buttonToggled(int, bool))); pb1.setCheckable(true); pb2.setCheckable(true); pb1.toggle(); QCOMPARE(toggledSpy.count(), 1); - QCOMPARE(toggledIdSpy.count(), 1); pb2.toggle(); QCOMPARE(toggledSpy.count(), 3); // equals 3 since pb1 and pb2 are both toggled - QCOMPARE(toggledIdSpy.count(), 3); pb1.setCheckable(false); pb2.setCheckable(false); pb1.toggle(); QCOMPARE(toggledSpy.count(), 3); - QCOMPARE(toggledIdSpy.count(), 3); } void tst_QButtonGroup::task106609() @@ -372,7 +348,6 @@ void tst_QButtonGroup::task106609() qRegisterMetaType<QAbstractButton*>("QAbstractButton*"); QSignalSpy spy1(buttons, SIGNAL(buttonClicked(QAbstractButton*))); - QSignalSpy spy2(buttons, SIGNAL(buttonClicked(int))); QApplication::setActiveWindow(&dlg); QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&dlg)); @@ -381,8 +356,6 @@ void tst_QButtonGroup::task106609() radio1->setChecked(true); QTestEventLoop::instance().enterLoop(1); - //qDebug() << "int:" << spy2.count() << "QAbstractButton*:" << spy1.count(); - QCOMPARE(spy2.count(), 2); QCOMPARE(spy1.count(), 2); } @@ -427,11 +400,12 @@ public: : group(group) , deleteButton(deleteButton) { - connect(group, SIGNAL(buttonClicked(int)), SLOT(buttonClicked(int))); + connect(group, &QButtonGroup::buttonClicked, + this, &task209485_ButtonDeleter::buttonClicked); } private slots: - void buttonClicked(int) + void buttonClicked() { if (deleteButton) group->removeButton(group->buttons().first()); @@ -447,7 +421,7 @@ void tst_QButtonGroup::task209485_removeFromGroupInEventHandler_data() QTest::addColumn<bool>("deleteButton"); QTest::addColumn<int>("signalCount"); QTest::newRow("buttonPress 1") << true << 1; - QTest::newRow("buttonPress 2") << false << 2; + QTest::newRow("buttonPress 2") << false << 1; } void tst_QButtonGroup::task209485_removeFromGroupInEventHandler() @@ -463,12 +437,11 @@ void tst_QButtonGroup::task209485_removeFromGroupInEventHandler() task209485_ButtonDeleter buttonDeleter(&group, deleteButton); QSignalSpy spy1(&group, SIGNAL(buttonClicked(QAbstractButton*))); - QSignalSpy spy2(&group, SIGNAL(buttonClicked(int))); // NOTE: Reintroducing the bug of this task will cause the following line to crash: QTest::mouseClick(button, Qt::LeftButton); - QCOMPARE(spy1.count() + spy2.count(), signalCount); + QCOMPARE(spy1.count(), signalCount); } void tst_QButtonGroup::autoIncrementId() |