From 5d1c35e21d2f5bcc3b4fa118d6b3be7982fd8207 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 11 Aug 2016 17:02:08 +0300 Subject: QTranslator: use const to avoid detach()'ing Change-Id: Idbab696affed9486e9077d72ba14ce409b98174c Reviewed-by: Edward Welbourne Reviewed-by: Thiago Macieira --- src/corelib/kernel/qtranslator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index e016773bde..04a5e7ed7a 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -489,8 +489,7 @@ bool QTranslator::load(const QString & filename, const QString & directory, const QString suffixOrDotQM = suffix.isNull() ? dotQmLiteral() : suffix; QStringRef fname(&filename); QString realname; - QString delims; - delims = search_delimiters.isNull() ? QStringLiteral("_.") : search_delimiters; + const QString delims = search_delimiters.isNull() ? QStringLiteral("_.") : search_delimiters; for (;;) { QFileInfo fi; -- cgit v1.2.3 From 131eee5cd7547ddb658d6337e1877da3d73b3158 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 31 Aug 2015 22:13:08 +0200 Subject: Avoid synthesizing stretch on condensed font families If an entire font family is condensed or stretched and we match by family name, the default stretch factor of 100 will make the font engine try to synthesize it back to medium stretched font. The existing code is already made to deal with a stretch of 0 that is no longer used. This patch reintroduces 0 stretch to indicate no specific stretch has been requested. Specifically setting stretch to 100 on a QFont will introduce the old behavior. [ChangeLog][QtGui][QFont] The default value of QFont::stretch() is now 0 to indicate any default stretch is acceptable. Task-number: QTBUG-48043 Change-Id: I574747f980fd4f9893df828818aae99a07b41623 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/gui/text/qfont.cpp | 16 +++++++++++----- src/gui/text/qfont.h | 1 + src/gui/text/qfont_p.h | 2 +- src/gui/text/qfontdatabase.cpp | 15 ++++++++------- .../fontdatabases/mac/qfontengine_coretext.mm | 2 +- tests/auto/gui/text/qfontcache/tst_qfontcache.cpp | 2 -- .../gui/text/qfontdatabase/tst_qfontdatabase.cpp | 20 ++++++++++++++++++++ 7 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 0fa32dc56d..37b8c22920 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -1376,6 +1376,7 @@ void QFont::setStyleStrategy(StyleStrategy s) Predefined stretch values that follow the CSS naming convention. The higher the value, the more stretched the text is. + \value AnyStretch 0 Accept any stretch matched using the other QFont properties (added in Qt 5.8) \value UltraCondensed 50 \value ExtraCondensed 62 \value Condensed 75 @@ -1402,20 +1403,25 @@ int QFont::stretch() const /*! Sets the stretch factor for the font. - The stretch factor changes the width of all characters in the font - by \a factor percent. For example, setting \a factor to 150 + The stretch factor matches a condensed or expanded version of the font or + applies a stretch transform that changes the width of all characters + in the font by \a factor percent. For example, setting \a factor to 150 results in all characters in the font being 1.5 times (ie. 150%) - wider. The default stretch factor is 100. The minimum stretch - factor is 1, and the maximum stretch factor is 4000. + wider. The minimum stretch factor is 1, and the maximum stretch factor + is 4000. The default stretch factor is \c AnyStretch, which will accept + any stretch factor and not apply any transform on the font. The stretch factor is only applied to outline fonts. The stretch factor is ignored for bitmap fonts. + \note When matching a font with a native non-default stretch factor, + requesting a stretch of 100 will stretch it back to a medium width font. + \sa stretch(), QFont::Stretch */ void QFont::setStretch(int factor) { - if (factor < 1 || factor > 4000) { + if (factor < 0 || factor > 4000) { qWarning("QFont::setStretch: Parameter '%d' out of range", factor); return; } diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index b295e13f61..6f0dd27fbe 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -113,6 +113,7 @@ public: }; enum Stretch { + AnyStretch = 0, UltraCondensed = 50, ExtraCondensed = 62, Condensed = 75, diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index 21823dc12f..9e5d0b4329 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -71,7 +71,7 @@ struct QFontDef inline QFontDef() : pointSize(-1.0), pixelSize(-1), styleStrategy(QFont::PreferDefault), styleHint(QFont::AnyStyle), - weight(50), fixedPitch(false), style(QFont::StyleNormal), stretch(100), + weight(50), fixedPitch(false), style(QFont::StyleNormal), stretch(QFont::AnyStretch), hintingPreference(QFont::PreferDefaultHinting), ignorePitch(true), fixedPitchComputed(0), reserved(0) { diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 594d791e9c..cb1619e690 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -951,12 +951,14 @@ QFontEngine *loadSingleEngine(int script, } } - // If the font data's native stretch matches the requested stretch we need to set stretch to 100 - // to avoid the fontengine synthesizing stretch. If they didn't match exactly we need to calculate - // the new stretch factor. This only done if not matched by styleName. + // To avoid synthesized stretch we need a matching stretch to be 100 after this point. + // If stretch didn't match exactly we need to calculate the new stretch factor. + // This only done if not matched by styleName. if (style->key.stretch != 0 && request.stretch != 0 && (request.styleName.isEmpty() || request.styleName != style->styleName)) { - def.stretch = (request.stretch * 100 + 50) / style->key.stretch; + def.stretch = (request.stretch * 100 + style->key.stretch / 2) / style->key.stretch; + } else { + def.stretch = 100; } engine = pfdb->fontEngine(def, size->handle); @@ -1219,7 +1221,8 @@ static int match(int script, const QFontDef &request, QtFontStyle::Key styleKey; styleKey.style = request.style; styleKey.weight = request.weight; - styleKey.stretch = request.stretch; + // Prefer a stretch closest to 100. + styleKey.stretch = request.stretch ? request.stretch : 100; char pitch = request.ignorePitch ? '*' : request.fixedPitch ? 'm' : 'p'; @@ -2740,8 +2743,6 @@ void QFontDatabase::load(const QFontPrivate *d, int script) } if (req.pointSize < 0) req.pointSize = req.pixelSize*72.0/d->dpi; - if (req.stretch == 0) - req.stretch = 100; // respect the fallback families that might be passed through the request const QStringList fallBackFamilies = familyList(req); diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm index a0047c0b7d..339212db25 100644 --- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm @@ -172,7 +172,7 @@ QFontEngine::GlyphFormat QCoreTextFontEngine::defaultGlyphFormat = QFontEngine:: CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef) { CGAffineTransform transform = CGAffineTransformIdentity; - if (fontDef.stretch != 100) + if (fontDef.stretch && fontDef.stretch != 100) transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); return transform; } diff --git a/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp index 54bc802cf0..fbca313ea3 100644 --- a/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp +++ b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp @@ -101,8 +101,6 @@ void tst_QFontCache::engineData() } if (req.pointSize < 0) req.pointSize = req.pixelSize*72.0/d->dpi; - if (req.stretch == 0) - req.stretch = 100; req.family = cacheKey; diff --git a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp index adaf3b1f7a..f71d808390 100644 --- a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp +++ b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp @@ -64,6 +64,8 @@ private slots: void aliases(); void fallbackFonts(); + void liberationFont(); + private: const QString m_testFont; }; @@ -275,5 +277,23 @@ void tst_QFontDatabase::fallbackFonts() } } +void tst_QFontDatabase::liberationFont() +{ + QString libSans("Liberation Sans"); + QString libSansNarrow("Liberation Sans Narrow"); + + QFontDatabase db; + if (!db.hasFamily(libSans) || !db.hasFamily(libSansNarrow)) + QSKIP("Requires Liberation Sans installed"); + + QFont fontLS(libSans); + QFont fontLSN(libSansNarrow); + + QFontMetrics fmLS(fontLS); + QFontMetrics fmLSN(fontLSN); + + QVERIFY(fmLS.width(QStringLiteral("foo bar")) > fmLSN.width(QStringLiteral("foo bar"))); +} + QTEST_MAIN(tst_QFontDatabase) #include "tst_qfontdatabase.moc" -- cgit v1.2.3 From ff57203b57751a38c814419495123d23bb7c3f1e Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Fri, 12 Aug 2016 10:16:46 +0200 Subject: Move QTriangulator and QTriangulatingStroker classes to painting Previously the private APIs for QTriangulator and QTriangulatingStroker were located in src/gui/opengl because they were used by the OpenGL paint engine. These API's are not actually specific to OpenGL however, and were not being built when QT_NO_OPENGL was defined. It makes more sense for these classes to belong in the painting subgroup. Aside from the OpenGL paint engine, these private APIs are used by QtLocation to triangulate polylines to be rendered by QtQuick. Change-Id: Idb4d1e5b2a51394d4c6bcdf9ab1ece99de23d4de Reviewed-by: Laszlo Agocs --- src/gui/opengl/opengl.pri | 5 - src/gui/opengl/qrbtree_p.h | 571 ------- src/gui/opengl/qtriangulatingstroker.cpp | 613 ------- src/gui/opengl/qtriangulatingstroker_p.h | 171 -- src/gui/opengl/qtriangulator.cpp | 2377 --------------------------- src/gui/opengl/qtriangulator_p.h | 148 -- src/gui/painting/painting.pri | 5 + src/gui/painting/qrbtree_p.h | 571 +++++++ src/gui/painting/qtriangulatingstroker.cpp | 613 +++++++ src/gui/painting/qtriangulatingstroker_p.h | 171 ++ src/gui/painting/qtriangulator.cpp | 2382 ++++++++++++++++++++++++++++ src/gui/painting/qtriangulator_p.h | 148 ++ 12 files changed, 3890 insertions(+), 3885 deletions(-) delete mode 100644 src/gui/opengl/qrbtree_p.h delete mode 100644 src/gui/opengl/qtriangulatingstroker.cpp delete mode 100644 src/gui/opengl/qtriangulatingstroker_p.h delete mode 100644 src/gui/opengl/qtriangulator.cpp delete mode 100644 src/gui/opengl/qtriangulator_p.h create mode 100644 src/gui/painting/qrbtree_p.h create mode 100644 src/gui/painting/qtriangulatingstroker.cpp create mode 100644 src/gui/painting/qtriangulatingstroker_p.h create mode 100644 src/gui/painting/qtriangulator.cpp create mode 100644 src/gui/painting/qtriangulator_p.h diff --git a/src/gui/opengl/opengl.pri b/src/gui/opengl/opengl.pri index dfaf3042bc..bdda5381ce 100644 --- a/src/gui/opengl/opengl.pri +++ b/src/gui/opengl/opengl.pri @@ -22,12 +22,9 @@ contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles2) { opengl/qopenglpaintengine_p.h \ opengl/qopenglengineshadersource_p.h \ opengl/qopenglcustomshaderstage_p.h \ - opengl/qtriangulatingstroker_p.h \ opengl/qopengltextureglyphcache_p.h \ opengl/qopenglshadercache_p.h \ opengl/qopenglshadercache_meego_p.h \ - opengl/qtriangulator_p.h \ - opengl/qrbtree_p.h \ opengl/qopenglversionfunctions.h \ opengl/qopenglversionfunctionsfactory_p.h \ opengl/qopenglvertexarrayobject.h \ @@ -51,9 +48,7 @@ contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles2) { opengl/qopengl2pexvertexarray.cpp \ opengl/qopenglpaintengine.cpp \ opengl/qopenglcustomshaderstage.cpp \ - opengl/qtriangulatingstroker.cpp \ opengl/qopengltextureglyphcache.cpp \ - opengl/qtriangulator.cpp \ opengl/qopenglversionfunctions.cpp \ opengl/qopenglversionfunctionsfactory.cpp \ opengl/qopenglvertexarrayobject.cpp \ diff --git a/src/gui/opengl/qrbtree_p.h b/src/gui/opengl/qrbtree_p.h deleted file mode 100644 index d3ee23a91c..0000000000 --- a/src/gui/opengl/qrbtree_p.h +++ /dev/null @@ -1,571 +0,0 @@ -/**************************************************************************** -** -** 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. -** -** $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 QRBTREE_P_H -#define QRBTREE_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 - -QT_BEGIN_NAMESPACE - -template -struct QRBTree -{ - struct Node - { - inline Node() : parent(0), left(0), right(0), red(true) { } - inline ~Node() {if (left) delete left; if (right) delete right;} - T data; - Node *parent; - Node *left; - Node *right; - bool red; - }; - - inline QRBTree() : root(0), freeList(0) { } - inline ~QRBTree(); - - inline void clear(); - - void attachBefore(Node *parent, Node *child); - void attachAfter(Node *parent, Node *child); - - inline Node *front(Node *node) const; - inline Node *back(Node *node) const; - Node *next(Node *node) const; - Node *previous(Node *node) const; - - inline void deleteNode(Node *&node); - inline Node *newNode(); - - // Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise. - // 'left' and 'right' cannot be null. - int order(Node *left, Node *right); - inline bool validate() const; - -private: - void rotateLeft(Node *node); - void rotateRight(Node *node); - void update(Node *node); - - inline void attachLeft(Node *parent, Node *child); - inline void attachRight(Node *parent, Node *child); - - int blackDepth(Node *top) const; - bool checkRedBlackProperty(Node *top) const; - - void swapNodes(Node *n1, Node *n2); - void detach(Node *node); - - // 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. - void rebalance(Node *node); - -public: - Node *root; -private: - Node *freeList; -}; - -template -inline QRBTree::~QRBTree() -{ - clear(); - while (freeList) { - // Avoid recursively calling the destructor, as this list may become large. - Node *next = freeList->right; - freeList->right = 0; - delete freeList; - freeList = next; - } -} - -template -inline void QRBTree::clear() -{ - if (root) - delete root; - root = 0; -} - -template -void QRBTree::rotateLeft(Node *node) -{ - // | | // - // N B // - // / \ / \ // - // A B ---> N D // - // / \ / \ // - // C D A C // - - Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); - ref = node->right; - node->right->parent = node->parent; - - // : // - // N // - // / :| // - // A B // - // / \ // - // C D // - - node->right = ref->left; - if (ref->left) - ref->left->parent = node; - - // : | // - // N B // - // / \ : \ // - // A C D // - - ref->left = node; - node->parent = ref; - - // | // - // B // - // / \ // - // N D // - // / \ // - // A C // -} - -template -void QRBTree::rotateRight(Node *node) -{ - // | | // - // N A // - // / \ / \ // - // A B ---> C N // - // / \ / \ // - // C D D B // - - Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); - ref = node->left; - node->left->parent = node->parent; - - node->left = ref->right; - if (ref->right) - ref->right->parent = node; - - ref->right = node; - node->parent = ref; -} - -template -void QRBTree::update(Node *node) // call this after inserting a node -{ - for (;;) { - Node *parent = node->parent; - - // if the node is the root, color it black - if (!parent) { - node->red = false; - return; - } - - // if the parent is black, the node can be left red - if (!parent->red) - return; - - // at this point, the parent is red and cannot be the root - Node *grandpa = parent->parent; - Q_ASSERT(grandpa); - - Node *uncle = (parent == grandpa->left ? grandpa->right : grandpa->left); - if (uncle && uncle->red) { - // grandpa's black, parent and uncle are red. - // let parent and uncle be black, grandpa red and recursively update grandpa. - Q_ASSERT(!grandpa->red); - parent->red = false; - uncle->red = false; - grandpa->red = true; - node = grandpa; - continue; - } - - // at this point, uncle is black - if (node == parent->right && parent == grandpa->left) - rotateLeft(node = parent); - else if (node == parent->left && parent == grandpa->right) - rotateRight(node = parent); - parent = node->parent; - - if (parent == grandpa->left) { - rotateRight(grandpa); - parent->red = false; - grandpa->red = true; - } else { - rotateLeft(grandpa); - parent->red = false; - grandpa->red = true; - } - return; - } -} - -template -inline void QRBTree::attachLeft(Node *parent, Node *child) -{ - Q_ASSERT(!parent->left); - parent->left = child; - child->parent = parent; - update(child); -} - -template -inline void QRBTree::attachRight(Node *parent, Node *child) -{ - Q_ASSERT(!parent->right); - parent->right = child; - child->parent = parent; - update(child); -} - -template -void QRBTree::attachBefore(Node *parent, Node *child) -{ - if (!root) - update(root = child); - else if (!parent) - attachRight(back(root), child); - else if (parent->left) - attachRight(back(parent->left), child); - else - attachLeft(parent, child); -} - -template -void QRBTree::attachAfter(Node *parent, Node *child) -{ - if (!root) - update(root = child); - else if (!parent) - attachLeft(front(root), child); - else if (parent->right) - attachLeft(front(parent->right), child); - else - attachRight(parent, child); -} - -template -void QRBTree::swapNodes(Node *n1, Node *n2) -{ - // Since iterators must not be invalidated, it is not sufficient to only swap the data. - if (n1->parent == n2) { - n1->parent = n2->parent; - n2->parent = n1; - } else if (n2->parent == n1) { - n2->parent = n1->parent; - n1->parent = n2; - } else { - qSwap(n1->parent, n2->parent); - } - - qSwap(n1->left, n2->left); - qSwap(n1->right, n2->right); - qSwap(n1->red, n2->red); - - if (n1->parent) { - if (n1->parent->left == n2) - n1->parent->left = n1; - else - n1->parent->right = n1; - } else { - root = n1; - } - - if (n2->parent) { - if (n2->parent->left == n1) - n2->parent->left = n2; - else - n2->parent->right = n2; - } else { - root = n2; - } - - if (n1->left) - n1->left->parent = n1; - if (n1->right) - n1->right->parent = n1; - - if (n2->left) - n2->left->parent = n2; - if (n2->right) - n2->right->parent = n2; -} - -template -void QRBTree::detach(Node *node) // call this before removing a node. -{ - if (node->right) - swapNodes(node, front(node->right)); - - Node *child = (node->left ? node->left : node->right); - - if (!node->red) { - if (child && child->red) - child->red = false; - else - rebalance(node); - } - - Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); - ref = child; - if (child) - child->parent = node->parent; - node->left = node->right = node->parent = 0; -} - -// 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. -template -void QRBTree::rebalance(Node *node) -{ - Q_ASSERT(!node->red); - for (;;) { - if (!node->parent) - return; - - // at this point, node is not a parent, it is black, thus it must have a sibling. - Node *sibling = (node == node->parent->left ? node->parent->right : node->parent->left); - Q_ASSERT(sibling); - - if (sibling->red) { - sibling->red = false; - node->parent->red = true; - if (node == node->parent->left) - rotateLeft(node->parent); - else - rotateRight(node->parent); - sibling = (node == node->parent->left ? node->parent->right : node->parent->left); - Q_ASSERT(sibling); - } - - // at this point, the sibling is black. - Q_ASSERT(!sibling->red); - - if ((!sibling->left || !sibling->left->red) && (!sibling->right || !sibling->right->red)) { - bool parentWasRed = node->parent->red; - sibling->red = true; - node->parent->red = false; - if (parentWasRed) - return; - node = node->parent; - continue; - } - - // at this point, at least one of the sibling's children is red. - - if (node == node->parent->left) { - if (!sibling->right || !sibling->right->red) { - Q_ASSERT(sibling->left); - sibling->red = true; - sibling->left->red = false; - rotateRight(sibling); - - sibling = sibling->parent; - Q_ASSERT(sibling); - } - sibling->red = node->parent->red; - node->parent->red = false; - - Q_ASSERT(sibling->right->red); - sibling->right->red = false; - rotateLeft(node->parent); - } else { - if (!sibling->left || !sibling->left->red) { - Q_ASSERT(sibling->right); - sibling->red = true; - sibling->right->red = false; - rotateLeft(sibling); - - sibling = sibling->parent; - Q_ASSERT(sibling); - } - sibling->red = node->parent->red; - node->parent->red = false; - - Q_ASSERT(sibling->left->red); - sibling->left->red = false; - rotateRight(node->parent); - } - return; - } -} - -template -inline typename QRBTree::Node *QRBTree::front(Node *node) const -{ - while (node->left) - node = node->left; - return node; -} - -template -inline typename QRBTree::Node *QRBTree::back(Node *node) const -{ - while (node->right) - node = node->right; - return node; -} - -template -typename QRBTree::Node *QRBTree::next(Node *node) const -{ - if (node->right) - return front(node->right); - while (node->parent && node == node->parent->right) - node = node->parent; - return node->parent; -} - -template -typename QRBTree::Node *QRBTree::previous(Node *node) const -{ - if (node->left) - return back(node->left); - while (node->parent && node == node->parent->left) - node = node->parent; - return node->parent; -} - -template -int QRBTree::blackDepth(Node *top) const -{ - if (!top) - return 0; - int leftDepth = blackDepth(top->left); - int rightDepth = blackDepth(top->right); - if (leftDepth != rightDepth) - return -1; - if (!top->red) - ++leftDepth; - return leftDepth; -} - -template -bool QRBTree::checkRedBlackProperty(Node *top) const -{ - if (!top) - return true; - if (top->left && !checkRedBlackProperty(top->left)) - return false; - if (top->right && !checkRedBlackProperty(top->right)) - return false; - return !(top->red && ((top->left && top->left->red) || (top->right && top->right->red))); -} - -template -inline bool QRBTree::validate() const -{ - return checkRedBlackProperty(root) && blackDepth(root) != -1; -} - -template -inline void QRBTree::deleteNode(Node *&node) -{ - Q_ASSERT(node); - detach(node); - node->right = freeList; - freeList = node; - node = 0; -} - -template -inline typename QRBTree::Node *QRBTree::newNode() -{ - if (freeList) { - Node *node = freeList; - freeList = freeList->right; - node->parent = node->left = node->right = 0; - node->red = true; - return node; - } - return new Node; -} - -// Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise. -// 'left' and 'right' cannot be null. -template -int QRBTree::order(Node *left, Node *right) -{ - Q_ASSERT(left && right); - if (left == right) - return 0; - - QVector leftAncestors; - QVector rightAncestors; - while (left) { - leftAncestors.push_back(left); - left = left->parent; - } - while (right) { - rightAncestors.push_back(right); - right = right->parent; - } - Q_ASSERT(leftAncestors.back() == root && rightAncestors.back() == root); - - while (!leftAncestors.empty() && !rightAncestors.empty() && leftAncestors.back() == rightAncestors.back()) { - leftAncestors.pop_back(); - rightAncestors.pop_back(); - } - - if (!leftAncestors.empty()) - return (leftAncestors.back() == leftAncestors.back()->parent->left ? -1 : 1); - - if (!rightAncestors.empty()) - return (rightAncestors.back() == rightAncestors.back()->parent->right ? -1 : 1); - - // The code should never reach this point. - Q_ASSERT(!leftAncestors.empty() || !rightAncestors.empty()); - return 0; -} - -QT_END_NAMESPACE - -#endif diff --git a/src/gui/opengl/qtriangulatingstroker.cpp b/src/gui/opengl/qtriangulatingstroker.cpp deleted file mode 100644 index d9a3231165..0000000000 --- a/src/gui/opengl/qtriangulatingstroker.cpp +++ /dev/null @@ -1,613 +0,0 @@ -/**************************************************************************** -** -** 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. -** -** $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 "qtriangulatingstroker_p.h" -#include - -QT_BEGIN_NAMESPACE - -#define CURVE_FLATNESS Q_PI / 8 - - - - -void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur, - bool implicitClose, bool endsAtStart) -{ - if (endsAtStart) { - join(start + 2); - } else if (implicitClose) { - join(start); - lineTo(start); - join(start+2); - } else { - endCap(cur); - } - int count = m_vertices.size(); - - // Copy the (x, y) values because QDataBuffer::add(const float& t) - // may resize the buffer, which will leave t pointing at the - // previous buffer's memory region if we don't copy first. - float x = m_vertices.at(count-2); - float y = m_vertices.at(count-1); - m_vertices.add(x); - m_vertices.add(y); -} - -static inline void skipDuplicatePoints(const qreal **pts, const qreal *endPts) -{ - while ((*pts + 2) < endPts && float((*pts)[0]) == float((*pts)[2]) - && float((*pts)[1]) == float((*pts)[3])) - { - *pts += 2; - } -} - -void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, const QRectF &, QPainter::RenderHints hints) -{ - const qreal *pts = path.points(); - const QPainterPath::ElementType *types = path.elements(); - int count = path.elementCount(); - if (count < 2) - return; - - float realWidth = qpen_widthf(pen); - if (realWidth == 0) - realWidth = 1; - - m_width = realWidth / 2; - - bool cosmetic = qt_pen_is_cosmetic(pen, hints); - if (cosmetic) { - m_width = m_width * m_inv_scale; - } - - m_join_style = qpen_joinStyle(pen); - m_cap_style = qpen_capStyle(pen); - m_vertices.reset(); - m_miter_limit = pen.miterLimit() * qpen_widthf(pen); - - // The curvyness is based on the notion that I originally wanted - // roughly one line segment pr 4 pixels. This may seem little, but - // because we sample at constantly incrementing B(t) E [0(4, realWidth * CURVE_FLATNESS); - } else { - m_curvyness_add = m_width; - m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; - m_roundness = qMax(4, realWidth * m_curvyness_mul); - } - - // Over this level of segmentation, there doesn't seem to be any - // benefit, even for huge penWidth - if (m_roundness > 24) - m_roundness = 24; - - m_sin_theta = qFastSin(Q_PI / m_roundness); - m_cos_theta = qFastCos(Q_PI / m_roundness); - - const qreal *endPts = pts + (count<<1); - const qreal *startPts = 0; - - Qt::PenCapStyle cap = m_cap_style; - - if (!types) { - skipDuplicatePoints(&pts, endPts); - if ((pts + 2) == endPts) - return; - - startPts = pts; - - bool endsAtStart = float(startPts[0]) == float(endPts[-2]) - && float(startPts[1]) == float(endPts[-1]); - - if (endsAtStart || path.hasImplicitClose()) - m_cap_style = Qt::FlatCap; - moveTo(pts); - m_cap_style = cap; - pts += 2; - skipDuplicatePoints(&pts, endPts); - lineTo(pts); - pts += 2; - skipDuplicatePoints(&pts, endPts); - while (pts < endPts) { - join(pts); - lineTo(pts); - pts += 2; - skipDuplicatePoints(&pts, endPts); - } - endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); - - } else { - bool endsAtStart = false; - QPainterPath::ElementType previousType = QPainterPath::MoveToElement; - const qreal *previousPts = pts; - while (pts < endPts) { - switch (*types) { - case QPainterPath::MoveToElement: { - if (previousType != QPainterPath::MoveToElement) - endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart); - - startPts = pts; - skipDuplicatePoints(&startPts, endPts); // Skip duplicates to find correct normal. - if (startPts + 2 >= endPts) - return; // Nothing to see here... - - int end = (endPts - pts) / 2; - int i = 2; // Start looking to ahead since we never have two moveto's in a row - while (i points; - arcPoints(m_cx, m_cy, m_cx + m_nvx, m_cy + m_nvy, m_cx - m_nvx, m_cy - m_nvy, points); - m_vertices.resize(m_vertices.size() + points.size() + 2 * int(invisibleJump)); - int count = m_vertices.size(); - int front = 0; - int end = points.size() / 2; - while (front != end) { - m_vertices.at(--count) = points[2 * end - 1]; - m_vertices.at(--count) = points[2 * end - 2]; - --end; - if (front == end) - break; - m_vertices.at(--count) = points[2 * front + 1]; - m_vertices.at(--count) = points[2 * front + 0]; - ++front; - } - - if (invisibleJump) { - m_vertices.at(count - 1) = m_vertices.at(count + 1); - m_vertices.at(count - 2) = m_vertices.at(count + 0); - } - break; } - default: break; // ssssh gcc... - } - emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); -} - -void QTriangulatingStroker::cubicTo(const qreal *pts) -{ - const QPointF *p = (const QPointF *) pts; - QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]); - - QRectF bounds = bezier.bounds(); - float rad = qMax(bounds.width(), bounds.height()); - int threshold = qMin(64, (rad + m_curvyness_add) * m_curvyness_mul); - if (threshold < 4) - threshold = 4; - qreal threshold_minus_1 = threshold - 1; - float vx, vy; - - float cx = m_cx, cy = m_cy; - float x, y; - - for (int i=1; i (pts[0], pts[1]) - normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy); - - switch (m_join_style) { - case Qt::BevelJoin: - break; - case Qt::SvgMiterJoin: - case Qt::MiterJoin: { - // Find out on which side the join should be. - int count = m_vertices.size(); - float prevNvx = m_vertices.at(count - 2) - m_cx; - float prevNvy = m_vertices.at(count - 1) - m_cy; - float xprod = prevNvx * m_nvy - prevNvy * m_nvx; - float px, py, qx, qy; - - // If the segments are parallel, use bevel join. - if (qFuzzyIsNull(xprod)) - break; - - // Find the corners of the previous and next segment to join. - if (xprod < 0) { - px = m_vertices.at(count - 2); - py = m_vertices.at(count - 1); - qx = m_cx - m_nvx; - qy = m_cy - m_nvy; - } else { - px = m_vertices.at(count - 4); - py = m_vertices.at(count - 3); - qx = m_cx + m_nvx; - qy = m_cy + m_nvy; - } - - // Find intersection point. - float pu = px * prevNvx + py * prevNvy; - float qv = qx * m_nvx + qy * m_nvy; - float ix = (m_nvy * pu - prevNvy * qv) / xprod; - float iy = (prevNvx * qv - m_nvx * pu) / xprod; - - // Check that the distance to the intersection point is less than the miter limit. - if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= m_miter_limit * m_miter_limit) { - m_vertices.add(ix); - m_vertices.add(iy); - m_vertices.add(ix); - m_vertices.add(iy); - } - // else - // Do a plain bevel join if the miter limit is exceeded or if - // the lines are parallel. This is not what the raster - // engine's stroker does, but it is both faster and similar to - // what some other graphics API's do. - - break; } - case Qt::RoundJoin: { - QVarLengthArray points; - int count = m_vertices.size(); - float prevNvx = m_vertices.at(count - 2) - m_cx; - float prevNvy = m_vertices.at(count - 1) - m_cy; - if (m_nvx * prevNvy - m_nvy * prevNvx < 0) { - arcPoints(0, 0, m_nvx, m_nvy, -prevNvx, -prevNvy, points); - for (int i = points.size() / 2; i > 0; --i) - emitLineSegment(m_cx, m_cy, points[2 * i - 2], points[2 * i - 1]); - } else { - arcPoints(0, 0, -prevNvx, -prevNvy, m_nvx, m_nvy, points); - for (int i = 0; i < points.size() / 2; ++i) - emitLineSegment(m_cx, m_cy, points[2 * i + 0], points[2 * i + 1]); - } - break; } - default: break; // gcc warn-- - } - - emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); -} - -void QTriangulatingStroker::endCap(const qreal *) -{ - switch (m_cap_style) { - case Qt::FlatCap: - break; - case Qt::SquareCap: - emitLineSegment(m_cx + m_nvy, m_cy - m_nvx, m_nvx, m_nvy); - break; - case Qt::RoundCap: { - QVarLengthArray points; - int count = m_vertices.size(); - arcPoints(m_cx, m_cy, m_vertices.at(count - 2), m_vertices.at(count - 1), m_vertices.at(count - 4), m_vertices.at(count - 3), points); - int front = 0; - int end = points.size() / 2; - while (front != end) { - m_vertices.add(points[2 * end - 2]); - m_vertices.add(points[2 * end - 1]); - --end; - if (front == end) - break; - m_vertices.add(points[2 * front + 0]); - m_vertices.add(points[2 * front + 1]); - ++front; - } - break; } - default: break; // to shut gcc up... - } -} - -void QTriangulatingStroker::arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray &points) -{ - float dx1 = fromX - cx; - float dy1 = fromY - cy; - float dx2 = toX - cx; - float dy2 = toY - cy; - - // while more than 180 degrees left: - while (dx1 * dy2 - dx2 * dy1 < 0) { - float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; - float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; - dx1 = tmpx; - dy1 = tmpy; - points.append(cx + dx1); - points.append(cy + dy1); - } - - // while more than 90 degrees left: - while (dx1 * dx2 + dy1 * dy2 < 0) { - float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; - float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; - dx1 = tmpx; - dy1 = tmpy; - points.append(cx + dx1); - points.append(cy + dy1); - } - - // while more than 0 degrees left: - while (dx1 * dy2 - dx2 * dy1 > 0) { - float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; - float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; - dx1 = tmpx; - dy1 = tmpy; - points.append(cx + dx1); - points.append(cy + dy1); - } - - // remove last point which was rotated beyond [toX, toY]. - if (!points.isEmpty()) - points.resize(points.size() - 2); -} - -static void qdashprocessor_moveTo(qreal x, qreal y, void *data) -{ - ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y); -} - -static void qdashprocessor_lineTo(qreal x, qreal y, void *data) -{ - ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y); -} - -static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *) -{ - Q_ASSERT(0); // The dasher should not produce curves... -} - -QDashedStrokeProcessor::QDashedStrokeProcessor() - : m_points(0), m_types(0), - m_dash_stroker(0), m_inv_scale(1) -{ - m_dash_stroker.setMoveToHook(qdashprocessor_moveTo); - m_dash_stroker.setLineToHook(qdashprocessor_lineTo); - m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo); -} - -void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints) -{ - - const qreal *pts = path.points(); - const QPainterPath::ElementType *types = path.elements(); - int count = path.elementCount(); - - bool cosmetic = qt_pen_is_cosmetic(pen, hints); - - m_points.reset(); - m_types.reset(); - m_points.reserve(path.elementCount()); - m_types.reserve(path.elementCount()); - - qreal width = qpen_widthf(pen); - if (width == 0) - width = 1; - - m_dash_stroker.setDashPattern(pen.dashPattern()); - m_dash_stroker.setStrokeWidth(cosmetic ? width * m_inv_scale : width); - m_dash_stroker.setDashOffset(pen.dashOffset()); - m_dash_stroker.setMiterLimit(pen.miterLimit()); - m_dash_stroker.setClipRect(clip); - - float curvynessAdd, curvynessMul; - - // simplify pens that are thin in device size (2px wide or less) - if (width < 2.5 && (cosmetic || m_inv_scale == 1)) { - curvynessAdd = 0.5; - curvynessMul = CURVE_FLATNESS / m_inv_scale; - } else if (cosmetic) { - curvynessAdd= width / 2; - curvynessMul= float(CURVE_FLATNESS); - } else { - curvynessAdd = width * m_inv_scale; - curvynessMul = CURVE_FLATNESS / m_inv_scale; - } - - if (count < 2) - return; - - const qreal *endPts = pts + (count<<1); - - m_dash_stroker.begin(this); - - if (!types) { - m_dash_stroker.moveTo(pts[0], pts[1]); - pts += 2; - while (pts < endPts) { - m_dash_stroker.lineTo(pts[0], pts[1]); - pts += 2; - } - } else { - while (pts < endPts) { - switch (*types) { - case QPainterPath::MoveToElement: - m_dash_stroker.moveTo(pts[0], pts[1]); - pts += 2; - ++types; - break; - case QPainterPath::LineToElement: - m_dash_stroker.lineTo(pts[0], pts[1]); - pts += 2; - ++types; - break; - case QPainterPath::CurveToElement: { - QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1), - *(((const QPointF *) pts)), - *(((const QPointF *) pts) + 1), - *(((const QPointF *) pts) + 2)); - QRectF bounds = b.bounds(); - float rad = qMax(bounds.width(), bounds.height()); - int threshold = qMin(64, (rad + curvynessAdd) * curvynessMul); - if (threshold < 4) - threshold = 4; - - qreal threshold_minus_1 = threshold - 1; - for (int i=0; i -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class Q_GUI_EXPORT QTriangulatingStroker -{ -public: - QTriangulatingStroker() : m_vertices(0), m_cx(0), m_cy(0), m_nvx(0), m_nvy(0), m_width(1), m_miter_limit(2), - m_roundness(0), m_sin_theta(0), m_cos_theta(0), m_inv_scale(1), m_curvyness_mul(1), m_curvyness_add(0), - m_join_style(Qt::BevelJoin), m_cap_style(Qt::SquareCap) {} - - void process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints); - - inline int vertexCount() const { return m_vertices.size(); } - inline const float *vertices() const { return m_vertices.data(); } - - inline void setInvScale(qreal invScale) { m_inv_scale = invScale; } - -private: - inline void emitLineSegment(float x, float y, float nx, float ny); - void moveTo(const qreal *pts); - inline void lineTo(const qreal *pts); - void cubicTo(const qreal *pts); - void join(const qreal *pts); - inline void normalVector(float x1, float y1, float x2, float y2, float *nx, float *ny); - void endCap(const qreal *pts); - void arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray &points); - void endCapOrJoinClosed(const qreal *start, const qreal *cur, bool implicitClose, bool endsAtStart); - - - QDataBuffer m_vertices; - - float m_cx, m_cy; // current points - float m_nvx, m_nvy; // normal vector... - float m_width; - qreal m_miter_limit; - - int m_roundness; // Number of line segments in a round join - qreal m_sin_theta; // sin(m_roundness / 360); - qreal m_cos_theta; // cos(m_roundness / 360); - qreal m_inv_scale; - float m_curvyness_mul; - float m_curvyness_add; - - Qt::PenJoinStyle m_join_style; - Qt::PenCapStyle m_cap_style; -}; - -class Q_GUI_EXPORT QDashedStrokeProcessor -{ -public: - QDashedStrokeProcessor(); - - void process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints); - - inline void addElement(QPainterPath::ElementType type, qreal x, qreal y) { - m_points.add(x); - m_points.add(y); - m_types.add(type); - } - - inline int elementCount() const { return m_types.size(); } - inline qreal *points() const { return m_points.data(); } - inline QPainterPath::ElementType *elementTypes() const { return m_types.data(); } - - inline void setInvScale(qreal invScale) { m_inv_scale = invScale; } - -private: - QDataBuffer m_points; - QDataBuffer m_types; - QDashStroker m_dash_stroker; - qreal m_inv_scale; -}; - -inline void QTriangulatingStroker::normalVector(float x1, float y1, float x2, float y2, - float *nx, float *ny) -{ - float dx = x2 - x1; - float dy = y2 - y1; - Q_ASSERT(dx != 0 || dy != 0); - - float pw; - - if (dx == 0) - pw = m_width / std::abs(dy); - else if (dy == 0) - pw = m_width / std::abs(dx); - else - pw = m_width / std::sqrt(dx*dx + dy*dy); - - *nx = -dy * pw; - *ny = dx * pw; -} - -inline void QTriangulatingStroker::emitLineSegment(float x, float y, float vx, float vy) -{ - m_vertices.add(x + vx); - m_vertices.add(y + vy); - m_vertices.add(x - vx); - m_vertices.add(y - vy); -} - -void QTriangulatingStroker::lineTo(const qreal *pts) -{ - emitLineSegment(pts[0], pts[1], m_nvx, m_nvy); - m_cx = pts[0]; - m_cy = pts[1]; -} - -QT_END_NAMESPACE - -#endif diff --git a/src/gui/opengl/qtriangulator.cpp b/src/gui/opengl/qtriangulator.cpp deleted file mode 100644 index 601b51a5fb..0000000000 --- a/src/gui/opengl/qtriangulator.cpp +++ /dev/null @@ -1,2377 +0,0 @@ -/**************************************************************************** -** -** 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. -** -** $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 "qtriangulator_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -//#define Q_TRIANGULATOR_DEBUG - -#define Q_FIXED_POINT_SCALE 32 - -template -struct QVertexSet -{ - inline QVertexSet() { } - inline QVertexSet(const QVertexSet &other) : vertices(other.vertices), indices(other.indices) { } - QVertexSet &operator = (const QVertexSet &other) {vertices = other.vertices; indices = other.indices; return *this;} - - // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ... - QVector vertices; // [x[0], y[0], x[1], y[1], x[2], ...] - QVector indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...] -}; - -//============================================================================// -// QFraction // -//============================================================================// - -// Fraction must be in the range [0, 1) -struct QFraction -{ - // Comparison operators must not be called on invalid fractions. - inline bool operator < (const QFraction &other) const; - inline bool operator == (const QFraction &other) const; - inline bool operator != (const QFraction &other) const {return !(*this == other);} - inline bool operator > (const QFraction &other) const {return other < *this;} - inline bool operator >= (const QFraction &other) const {return !(*this < other);} - inline bool operator <= (const QFraction &other) const {return !(*this > other);} - - inline bool isValid() const {return denominator != 0;} - - // numerator and denominator must not have common denominators. - quint64 numerator, denominator; -}; - -static inline quint64 gcd(quint64 x, quint64 y) -{ - while (y != 0) { - quint64 z = y; - y = x % y; - x = z; - } - return x; -} - -static inline int compare(quint64 a, quint64 b) -{ - return (a > b) - (a < b); -} - -// Compare a/b with c/d. -// Return negative if less, 0 if equal, positive if greater. -// a < b, c < d -static int qCompareFractions(quint64 a, quint64 b, quint64 c, quint64 d) -{ - const quint64 LIMIT = Q_UINT64_C(0x100000000); - for (;;) { - // If the products 'ad' and 'bc' fit into 64 bits, they can be directly compared. - if (b < LIMIT && d < LIMIT) - return compare(a * d, b * c); - - if (a == 0 || c == 0) - return compare(a, c); - - // a/b < c/d <=> d/c < b/a - quint64 b_div_a = b / a; - quint64 d_div_c = d / c; - if (b_div_a != d_div_c) - return compare(d_div_c, b_div_a); - - // floor(d/c) == floor(b/a) - // frac(d/c) < frac(b/a) ? - // frac(x/y) = (x%y)/y - d -= d_div_c * c; //d %= c; - b -= b_div_a * a; //b %= a; - qSwap(a, d); - qSwap(b, c); - } -} - -// Fraction must be in the range [0, 1) -// Assume input is valid. -static QFraction qFraction(quint64 n, quint64 d) { - QFraction result; - if (n == 0) { - result.numerator = 0; - result.denominator = 1; - } else { - quint64 g = gcd(n, d); - result.numerator = n / g; - result.denominator = d / g; - } - return result; -} - -inline bool QFraction::operator < (const QFraction &other) const -{ - return qCompareFractions(numerator, denominator, other.numerator, other.denominator) < 0; -} - -inline bool QFraction::operator == (const QFraction &other) const -{ - return numerator == other.numerator && denominator == other.denominator; -} - -//============================================================================// -// QPodPoint // -//============================================================================// - -struct QPodPoint -{ - inline bool operator < (const QPodPoint &other) const - { - if (y != other.y) - return y < other.y; - return x < other.x; - } - - inline bool operator > (const QPodPoint &other) const {return other < *this;} - inline bool operator <= (const QPodPoint &other) const {return !(*this > other);} - inline bool operator >= (const QPodPoint &other) const {return !(*this < other);} - inline bool operator == (const QPodPoint &other) const {return x == other.x && y == other.y;} - inline bool operator != (const QPodPoint &other) const {return x != other.x || y != other.y;} - - inline QPodPoint &operator += (const QPodPoint &other) {x += other.x; y += other.y; return *this;} - inline QPodPoint &operator -= (const QPodPoint &other) {x -= other.x; y -= other.y; return *this;} - inline QPodPoint operator + (const QPodPoint &other) const {QPodPoint result = {x + other.x, y + other.y}; return result;} - inline QPodPoint operator - (const QPodPoint &other) const {QPodPoint result = {x - other.x, y - other.y}; return result;} - - int x; - int y; -}; - -static inline qint64 qCross(const QPodPoint &u, const QPodPoint &v) -{ - return qint64(u.x) * qint64(v.y) - qint64(u.y) * qint64(v.x); -} - -#ifdef Q_TRIANGULATOR_DEBUG -static inline qint64 qDot(const QPodPoint &u, const QPodPoint &v) -{ - return qint64(u.x) * qint64(v.x) + qint64(u.y) * qint64(v.y); -} -#endif - -// Return positive value if 'p' is to the right of the line 'v1'->'v2', negative if left of the -// line and zero if exactly on the line. -// The returned value is the z-component of the qCross product between 'v2-v1' and 'p-v1', -// which is twice the signed area of the triangle 'p'->'v1'->'v2' (positive for CW order). -static inline qint64 qPointDistanceFromLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2) -{ - return qCross(v2 - v1, p - v1); -} - -static inline bool qPointIsLeftOfLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2) -{ - return QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(p, v1, v2) < 0; -} - -//============================================================================// -// QIntersectionPoint // -//============================================================================// - -struct QIntersectionPoint -{ - inline bool isValid() const {return xOffset.isValid() && yOffset.isValid();} - QPodPoint round() const; - inline bool isAccurate() const {return xOffset.numerator == 0 && yOffset.numerator == 0;} - bool operator < (const QIntersectionPoint &other) const; - bool operator == (const QIntersectionPoint &other) const; - inline bool operator != (const QIntersectionPoint &other) const {return !(*this == other);} - inline bool operator > (const QIntersectionPoint &other) const {return other < *this;} - inline bool operator >= (const QIntersectionPoint &other) const {return !(*this < other);} - inline bool operator <= (const QIntersectionPoint &other) const {return !(*this > other);} - bool isOnLine(const QPodPoint &u, const QPodPoint &v) const; - - QPodPoint upperLeft; - QFraction xOffset; - QFraction yOffset; -}; - -static inline QIntersectionPoint qIntersectionPoint(const QPodPoint &point) -{ - // upperLeft = point, xOffset = 0/1, yOffset = 0/1. - QIntersectionPoint p = {{point.x, point.y}, {0, 1}, {0, 1}}; - return p; -} - -static QIntersectionPoint qIntersectionPoint(const QPodPoint &u1, const QPodPoint &u2, const QPodPoint &v1, const QPodPoint &v2) -{ - QIntersectionPoint result = {{0, 0}, {0, 0}, {0, 0}}; - - QPodPoint u = u2 - u1; - QPodPoint v = v2 - v1; - qint64 d1 = qCross(u, v1 - u1); - qint64 d2 = qCross(u, v2 - u1); - qint64 det = d2 - d1; - qint64 d3 = qCross(v, u1 - v1); - qint64 d4 = d3 - det; //qCross(v, u2 - v1); - - // Check that the math is correct. - Q_ASSERT(d4 == qCross(v, u2 - v1)); - - // The intersection point can be expressed as: - // v1 - v * d1/det - // v2 - v * d2/det - // u1 + u * d3/det - // u2 + u * d4/det - - // I'm only interested in lines that are crossing, so ignore parallel lines even if they overlap. - if (det == 0) - return result; - - if (det < 0) { - det = -det; - d1 = -d1; - d2 = -d2; - d3 = -d3; - d4 = -d4; - } - - // I'm only interested in lines intersecting at their interior, not at their end points. - // The lines intersect at their interior if and only if 'd1 < 0', 'd2 > 0', 'd3 < 0' and 'd4 > 0'. - if (d1 >= 0 || d2 <= 0 || d3 <= 0 || d4 >= 0) - return result; - - // Calculate the intersection point as follows: - // v1 - v * d1/det | v1 <= v2 (component-wise) - // v2 - v * d2/det | v2 < v1 (component-wise) - - // Assuming 21 bits per vector component. - // TODO: Make code path for 31 bits per vector component. - if (v.x >= 0) { - result.upperLeft.x = v1.x + (-v.x * d1) / det; - result.xOffset = qFraction(quint64(-v.x * d1) % quint64(det), quint64(det)); - } else { - result.upperLeft.x = v2.x + (-v.x * d2) / det; - result.xOffset = qFraction(quint64(-v.x * d2) % quint64(det), quint64(det)); - } - - if (v.y >= 0) { - result.upperLeft.y = v1.y + (-v.y * d1) / det; - result.yOffset = qFraction(quint64(-v.y * d1) % quint64(det), quint64(det)); - } else { - result.upperLeft.y = v2.y + (-v.y * d2) / det; - result.yOffset = qFraction(quint64(-v.y * d2) % quint64(det), quint64(det)); - } - - Q_ASSERT(result.xOffset.isValid()); - Q_ASSERT(result.yOffset.isValid()); - return result; -} - -QPodPoint QIntersectionPoint::round() const -{ - QPodPoint result = upperLeft; - if (2 * xOffset.numerator >= xOffset.denominator) - ++result.x; - if (2 * yOffset.numerator >= yOffset.denominator) - ++result.y; - return result; -} - -bool QIntersectionPoint::operator < (const QIntersectionPoint &other) const -{ - if (upperLeft.y != other.upperLeft.y) - return upperLeft.y < other.upperLeft.y; - if (yOffset != other.yOffset) - return yOffset < other.yOffset; - if (upperLeft.x != other.upperLeft.x) - return upperLeft.x < other.upperLeft.x; - return xOffset < other.xOffset; -} - -bool QIntersectionPoint::operator == (const QIntersectionPoint &other) const -{ - return upperLeft == other.upperLeft && xOffset == other.xOffset && yOffset == other.yOffset; -} - -// Returns \c true if this point is on the infinite line passing through 'u' and 'v'. -bool QIntersectionPoint::isOnLine(const QPodPoint &u, const QPodPoint &v) const -{ - // TODO: Make code path for coordinates with more than 21 bits. - const QPodPoint p = upperLeft - u; - const QPodPoint q = v - u; - bool isHorizontal = p.y == 0 && yOffset.numerator == 0; - bool isVertical = p.x == 0 && xOffset.numerator == 0; - if (isHorizontal && isVertical) - return true; - if (isHorizontal) - return q.y == 0; - if (q.y == 0) - return false; - if (isVertical) - return q.x == 0; - if (q.x == 0) - return false; - - // At this point, 'p+offset' and 'q' cannot lie on the x or y axis. - - if (((q.x < 0) == (q.y < 0)) != ((p.x < 0) == (p.y < 0))) - return false; // 'p + offset' and 'q' pass through different quadrants. - - // Move all coordinates into the first quadrant. - quint64 nx, ny; - if (p.x < 0) - nx = quint64(-p.x) * xOffset.denominator - xOffset.numerator; - else - nx = quint64(p.x) * xOffset.denominator + xOffset.numerator; - if (p.y < 0) - ny = quint64(-p.y) * yOffset.denominator - yOffset.numerator; - else - ny = quint64(p.y) * yOffset.denominator + yOffset.numerator; - - return qFraction(quint64(qAbs(q.x)) * xOffset.denominator, quint64(qAbs(q.y)) * yOffset.denominator) == qFraction(nx, ny); -} - -//============================================================================// -// QMaxHeap // -//============================================================================// - -template -class QMaxHeap -{ -public: - QMaxHeap() : m_data(0) {} - inline int size() const {return m_data.size();} - inline bool empty() const {return m_data.isEmpty();} - inline bool isEmpty() const {return m_data.isEmpty();} - void push(const T &x); - T pop(); - inline const T &top() const {return m_data.first();} -private: - static inline int parent(int i) {return (i - 1) / 2;} - static inline int left(int i) {return 2 * i + 1;} - static inline int right(int i) {return 2 * i + 2;} - - QDataBuffer m_data; -}; - -template -void QMaxHeap::push(const T &x) -{ - int current = m_data.size(); - int parent = QMaxHeap::parent(current); - m_data.add(x); - while (current != 0 && m_data.at(parent) < x) { - m_data.at(current) = m_data.at(parent); - current = parent; - parent = QMaxHeap::parent(current); - } - m_data.at(current) = x; -} - -template -T QMaxHeap::pop() -{ - T result = m_data.first(); - T back = m_data.last(); - m_data.pop_back(); - if (!m_data.isEmpty()) { - int current = 0; - for (;;) { - int left = QMaxHeap::left(current); - int right = QMaxHeap::right(current); - if (left >= m_data.size()) - break; - int greater = left; - if (right < m_data.size() && m_data.at(left) < m_data.at(right)) - greater = right; - if (m_data.at(greater) < back) - break; - m_data.at(current) = m_data.at(greater); - current = greater; - } - m_data.at(current) = back; - } - return result; -} - -//============================================================================// -// QInt64Hash // -//============================================================================// - -// Copied from qhash.cpp -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 17, 27, 3, - 1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0 -}; - -// Copied from qhash.cpp -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - -static inline int primeForCount(int count) -{ - int low = 0; - int high = 32; - for (int i = 0; i < 5; ++i) { - int mid = (high + low) / 2; - if (uint(count) >= (1u << mid)) - low = mid; - else - high = mid; - } - return primeForNumBits(high); -} - -// Hash set of quint64s. Elements cannot be removed without clearing the -// entire set. A value of -1 is used to mark unused entries. -class QInt64Set -{ -public: - inline QInt64Set(int capacity = 64); - inline ~QInt64Set() {if (m_array) delete[] m_array;} - inline bool isValid() const {return m_array;} - void insert(quint64 key); - bool contains(quint64 key) const; - inline void clear(); -private: - bool rehash(int capacity); - - static const quint64 UNUSED; - - quint64 *m_array; - int m_capacity; - int m_count; -}; - -const quint64 QInt64Set::UNUSED = quint64(-1); - -inline QInt64Set::QInt64Set(int capacity) -{ - m_capacity = primeForCount(capacity); - m_array = new quint64[m_capacity]; - if (m_array) - clear(); - else - m_capacity = 0; -} - -bool QInt64Set::rehash(int capacity) -{ - quint64 *oldArray = m_array; - int oldCapacity = m_capacity; - - m_capacity = capacity; - m_array = new quint64[m_capacity]; - if (m_array) { - clear(); - if (oldArray) { - for (int i = 0; i < oldCapacity; ++i) { - if (oldArray[i] != UNUSED) - insert(oldArray[i]); - } - delete[] oldArray; - } - return true; - } else { - m_capacity = oldCapacity; - m_array = oldArray; - return false; - } -} - -void QInt64Set::insert(quint64 key) -{ - if (m_count > 3 * m_capacity / 4) - rehash(primeForCount(2 * m_capacity)); - Q_ASSERT_X(m_array, "QInt64Hash::insert", "Hash set not allocated."); - int index = int(key % m_capacity); - for (int i = 0; i < m_capacity; ++i) { - index += i; - if (index >= m_capacity) - index -= m_capacity; - if (m_array[index] == key) - return; - if (m_array[index] == UNUSED) { - ++m_count; - m_array[index] = key; - return; - } - } - Q_ASSERT_X(0, "QInt64Hash::insert", "Hash set full."); -} - -bool QInt64Set::contains(quint64 key) const -{ - Q_ASSERT_X(m_array, "QInt64Hash::contains", "Hash set not allocated."); - int index = int(key % m_capacity); - for (int i = 0; i < m_capacity; ++i) { - index += i; - if (index >= m_capacity) - index -= m_capacity; - if (m_array[index] == key) - return true; - if (m_array[index] == UNUSED) - return false; - } - return false; -} - -inline void QInt64Set::clear() -{ - Q_ASSERT_X(m_array, "QInt64Hash::clear", "Hash set not allocated."); - for (int i = 0; i < m_capacity; ++i) - m_array[i] = UNUSED; - m_count = 0; -} - -//============================================================================// -// QTriangulator // -//============================================================================// -template -class QTriangulator -{ -public: - typedef QVarLengthArray ShortArray; - - //================================// - // QTriangulator::ComplexToSimple // - //================================// - friend class ComplexToSimple; - class ComplexToSimple - { - public: - inline ComplexToSimple(QTriangulator *parent) : m_parent(parent), - m_edges(0), m_events(0), m_splits(0) { } - void decompose(); - private: - struct Edge - { - inline int &upper() {return pointingUp ? to : from;} - inline int &lower() {return pointingUp ? from : to;} - inline int upper() const {return pointingUp ? to : from;} - inline int lower() const {return pointingUp ? from : to;} - - QRBTree::Node *node; - int from, to; // vertex - int next, previous; // edge - int winding; - bool mayIntersect; - bool pointingUp, originallyPointingUp; - }; - - struct Intersection - { - bool operator < (const Intersection &other) const {return other.intersectionPoint < intersectionPoint;} - - QIntersectionPoint intersectionPoint; - int vertex; - int leftEdge; - int rightEdge; - }; - - struct Split - { - int vertex; - int edge; - bool accurate; - }; - - struct Event - { - enum Type {Upper, Lower}; - inline bool operator < (const Event &other) const; - - QPodPoint point; - Type type; - int edge; - }; - -#ifdef Q_TRIANGULATOR_DEBUG - friend class DebugDialog; - friend class QTriangulator; - class DebugDialog : public QDialog - { - public: - DebugDialog(ComplexToSimple *parent, int currentVertex); - protected: - void paintEvent(QPaintEvent *); - void wheelEvent(QWheelEvent *); - void mouseMoveEvent(QMouseEvent *); - void mousePressEvent(QMouseEvent *); - private: - ComplexToSimple *m_parent; - QRectF m_window; - QPoint m_lastMousePos; - int m_vertex; - }; -#endif - - void initEdges(); - bool calculateIntersection(int left, int right); - bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const; - QRBTree::Node *searchEdgeLeftOf(int edgeIndex) const; - QRBTree::Node *searchEdgeLeftOf(int edgeIndex, QRBTree::Node *after) const; - QPair::Node *, QRBTree::Node *> bounds(const QPodPoint &point) const; - QPair::Node *, QRBTree::Node *> outerBounds(const QPodPoint &point) const; - void splitEdgeListRange(QRBTree::Node *leftmost, QRBTree::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint); - void reorderEdgeListRange(QRBTree::Node *leftmost, QRBTree::Node *rightmost); - void sortEdgeList(const QPodPoint eventPoint); - void fillPriorityQueue(); - void calculateIntersections(); - int splitEdge(int splitIndex); - bool splitEdgesAtIntersections(); - void insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i); - void removeUnwantedEdgesAndConnect(); - void removeUnusedPoints(); - - QTriangulator *m_parent; - QDataBuffer m_edges; - QRBTree m_edgeList; - QDataBuffer m_events; - QDataBuffer m_splits; - QMaxHeap m_topIntersection; - QInt64Set m_processedEdgePairs; - int m_initialPointCount; - }; -#ifdef Q_TRIANGULATOR_DEBUG - friend class ComplexToSimple::DebugDialog; -#endif - - //=================================// - // QTriangulator::SimpleToMonotone // - //=================================// - friend class SimpleToMonotone; - class SimpleToMonotone - { - public: - inline SimpleToMonotone(QTriangulator *parent) : m_parent(parent), m_edges(0), m_upperVertex(0) { } - void decompose(); - private: - enum VertexType {MergeVertex, EndVertex, RegularVertex, StartVertex, SplitVertex}; - - struct Edge - { - QRBTree::Node *node; - int helper, twin, next, previous; - T from, to; - VertexType type; - bool pointingUp; - int upper() const {return (pointingUp ? to : from);} - int lower() const {return (pointingUp ? from : to);} - }; - - friend class CompareVertices; - class CompareVertices - { - public: - CompareVertices(SimpleToMonotone *parent) : m_parent(parent) { } - bool operator () (int i, int j) const; - private: - SimpleToMonotone *m_parent; - }; - - void setupDataStructures(); - void removeZeroLengthEdges(); - void fillPriorityQueue(); - bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const; - // Returns the rightmost edge not to the right of the given edge. - QRBTree::Node *searchEdgeLeftOfEdge(int edgeIndex) const; - // Returns the rightmost edge left of the given point. - QRBTree::Node *searchEdgeLeftOfPoint(int pointIndex) const; - void classifyVertex(int i); - void classifyVertices(); - bool pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3); - bool pointIsInSector(int vertex, int sector); - int findSector(int edge, int vertex); - void createDiagonal(int lower, int upper); - void monotoneDecomposition(); - - QTriangulator *m_parent; - QRBTree m_edgeList; - QDataBuffer m_edges; - QDataBuffer m_upperVertex; - bool m_clockwiseOrder; - }; - - //====================================// - // QTriangulator::MonotoneToTriangles // - //====================================// - friend class MonotoneToTriangles; - class MonotoneToTriangles - { - public: - inline MonotoneToTriangles(QTriangulator *parent) : m_parent(parent) { } - void decompose(); - private: - inline T indices(int index) const {return m_parent->m_indices.at(index + m_first);} - inline int next(int index) const {return (index + 1) % m_length;} - inline int previous(int index) const {return (index + m_length - 1) % m_length;} - inline bool less(int i, int j) const {return m_parent->m_vertices.at((qint32)indices(i)) < m_parent->m_vertices.at(indices(j));} - inline bool leftOfEdge(int i, int j, int k) const - { - return qPointIsLeftOfLine(m_parent->m_vertices.at((qint32)indices(i)), - m_parent->m_vertices.at((qint32)indices(j)), m_parent->m_vertices.at((qint32)indices(k))); - } - - QTriangulator *m_parent; - int m_first; - int m_length; - }; - - inline QTriangulator() : m_vertices(0) { } - - // Call this only once. - void initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix); - // Call this only once. - void initialize(const QVectorPath &path, const QTransform &matrix, qreal lod); - // Call this only once. - void initialize(const QPainterPath &path, const QTransform &matrix, qreal lod); - // Call either triangulate() or polyline() only once. - QVertexSet triangulate(); - QVertexSet polyline(); -private: - QDataBuffer m_vertices; - QVector m_indices; - uint m_hint; -}; - -//============================================================================// -// QTriangulator // -//============================================================================// - -template -QVertexSet QTriangulator::triangulate() -{ - for (int i = 0; i < m_vertices.size(); ++i) { - Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21)); - Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21)); - } - - if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill))) - m_hint |= QVectorPath::OddEvenFill; - - if (m_hint & QVectorPath::NonConvexShapeMask) { - ComplexToSimple c2s(this); - c2s.decompose(); - SimpleToMonotone s2m(this); - s2m.decompose(); - } - MonotoneToTriangles m2t(this); - m2t.decompose(); - - QVertexSet result; - result.indices = m_indices; - result.vertices.resize(2 * m_vertices.size()); - for (int i = 0; i < m_vertices.size(); ++i) { - result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE; - result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE; - } - return result; -} - -template -QVertexSet QTriangulator::polyline() -{ - for (int i = 0; i < m_vertices.size(); ++i) { - Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21)); - Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21)); - } - - if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill))) - m_hint |= QVectorPath::OddEvenFill; - - if (m_hint & QVectorPath::NonConvexShapeMask) { - ComplexToSimple c2s(this); - c2s.decompose(); - } - - QVertexSet result; - result.indices = m_indices; - result.vertices.resize(2 * m_vertices.size()); - for (int i = 0; i < m_vertices.size(); ++i) { - result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE; - result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE; - } - return result; -} - -template -void QTriangulator::initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix) -{ - m_hint = hint; - m_vertices.resize(count); - m_indices.resize(count + 1); - for (int i = 0; i < count; ++i) { - qreal x, y; - matrix.map(polygon[2 * i + 0], polygon[2 * i + 1], &x, &y); - m_vertices.at(i).x = qRound(x * Q_FIXED_POINT_SCALE); - m_vertices.at(i).y = qRound(y * Q_FIXED_POINT_SCALE); - m_indices[i] = i; - } - m_indices[count] = T(-1); //Q_TRIANGULATE_END_OF_POLYGON -} - -template -void QTriangulator::initialize(const QVectorPath &path, const QTransform &matrix, qreal lod) -{ - m_hint = path.hints(); - // Curved paths will be converted to complex polygons. - m_hint &= ~QVectorPath::CurvedShapeMask; - - const qreal *p = path.points(); - const QPainterPath::ElementType *e = path.elements(); - if (e) { - for (int i = 0; i < path.elementCount(); ++i, ++e, p += 2) { - switch (*e) { - case QPainterPath::MoveToElement: - if (!m_indices.isEmpty()) - m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON - // Fall through. - case QPainterPath::LineToElement: - m_indices.push_back(T(m_vertices.size())); - m_vertices.resize(m_vertices.size() + 1); - qreal x, y; - matrix.map(p[0], p[1], &x, &y); - m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE); - m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE); - break; - case QPainterPath::CurveToElement: - { - qreal pts[8]; - for (int i = 0; i < 4; ++i) - matrix.map(p[2 * i - 2], p[2 * i - 1], &pts[2 * i + 0], &pts[2 * i + 1]); - for (int i = 0; i < 8; ++i) - pts[i] *= lod; - QBezier bezier = QBezier::fromPoints(QPointF(pts[0], pts[1]), QPointF(pts[2], pts[3]), QPointF(pts[4], pts[5]), QPointF(pts[6], pts[7])); - QPolygonF poly = bezier.toPolygon(); - // Skip first point, it already exists in 'm_vertices'. - for (int j = 1; j < poly.size(); ++j) { - m_indices.push_back(T(m_vertices.size())); - m_vertices.resize(m_vertices.size() + 1); - m_vertices.last().x = qRound(poly.at(j).x() * Q_FIXED_POINT_SCALE / lod); - m_vertices.last().y = qRound(poly.at(j).y() * Q_FIXED_POINT_SCALE / lod); - } - } - i += 2; - e += 2; - p += 4; - break; - default: - Q_ASSERT_X(0, "QTriangulator::triangulate", "Unexpected element type."); - break; - } - } - } else { - for (int i = 0; i < path.elementCount(); ++i, p += 2) { - m_indices.push_back(T(m_vertices.size())); - m_vertices.resize(m_vertices.size() + 1); - qreal x, y; - matrix.map(p[0], p[1], &x, &y); - m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE); - m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE); - } - } - m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON -} - -template -void QTriangulator::initialize(const QPainterPath &path, const QTransform &matrix, qreal lod) -{ - initialize(qtVectorPathForPath(path), matrix, lod); -} - -//============================================================================// -// QTriangulator::ComplexToSimple // -//============================================================================// -template -void QTriangulator::ComplexToSimple::decompose() -{ - m_initialPointCount = m_parent->m_vertices.size(); - initEdges(); - do { - calculateIntersections(); - } while (splitEdgesAtIntersections()); - - removeUnwantedEdgesAndConnect(); - removeUnusedPoints(); - - m_parent->m_indices.clear(); - QBitArray processed(m_edges.size(), false); - for (int first = 0; first < m_edges.size(); ++first) { - // If already processed, or if unused path, skip. - if (processed.at(first) || m_edges.at(first).next == -1) - continue; - - int i = first; - do { - Q_ASSERT(!processed.at(i)); - Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i); - m_parent->m_indices.push_back(m_edges.at(i).from); - processed.setBit(i); - i = m_edges.at(i).next; // CCW order - } while (i != first); - m_parent->m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON - } -} - -template -void QTriangulator::ComplexToSimple::initEdges() -{ - // Initialize edge structure. - // 'next' and 'previous' are not being initialized at this point. - int first = 0; - for (int i = 0; i < m_parent->m_indices.size(); ++i) { - if (m_parent->m_indices.at(i) == T(-1)) { // Q_TRIANGULATE_END_OF_POLYGON - if (m_edges.size() != first) - m_edges.last().to = m_edges.at(first).from; - first = m_edges.size(); - } else { - Q_ASSERT(i + 1 < m_parent->m_indices.size()); - // {node, from, to, next, previous, winding, mayIntersect, pointingUp, originallyPointingUp} - Edge edge = {0, int(m_parent->m_indices.at(i)), int(m_parent->m_indices.at(i + 1)), -1, -1, 0, true, false, false}; - m_edges.add(edge); - } - } - if (first != m_edges.size()) - m_edges.last().to = m_edges.at(first).from; - for (int i = 0; i < m_edges.size(); ++i) { - m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp = - m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); - } -} - -// Return true if new intersection was found -template -bool QTriangulator::ComplexToSimple::calculateIntersection(int left, int right) -{ - const Edge &e1 = m_edges.at(left); - const Edge &e2 = m_edges.at(right); - - const QPodPoint &u1 = m_parent->m_vertices.at((qint32)e1.from); - const QPodPoint &u2 = m_parent->m_vertices.at((qint32)e1.to); - const QPodPoint &v1 = m_parent->m_vertices.at((qint32)e2.from); - const QPodPoint &v2 = m_parent->m_vertices.at((qint32)e2.to); - if (qMax(u1.x, u2.x) <= qMin(v1.x, v2.x)) - return false; - - quint64 key = (left > right ? (quint64(right) << 32) | quint64(left) : (quint64(left) << 32) | quint64(right)); - if (m_processedEdgePairs.contains(key)) - return false; - m_processedEdgePairs.insert(key); - - Intersection intersection; - intersection.leftEdge = left; - intersection.rightEdge = right; - intersection.intersectionPoint = QT_PREPEND_NAMESPACE(qIntersectionPoint)(u1, u2, v1, v2); - - if (!intersection.intersectionPoint.isValid()) - return false; - - Q_ASSERT(intersection.intersectionPoint.isOnLine(u1, u2)); - Q_ASSERT(intersection.intersectionPoint.isOnLine(v1, v2)); - - intersection.vertex = m_parent->m_vertices.size(); - m_topIntersection.push(intersection); - m_parent->m_vertices.add(intersection.intersectionPoint.round()); - return true; -} - -template -bool QTriangulator::ComplexToSimple::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const -{ - const Edge &leftEdge = m_edges.at(leftEdgeIndex); - const Edge &rightEdge = m_edges.at(rightEdgeIndex); - const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper()); - const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower()); - const QPodPoint &upper = m_parent->m_vertices.at(leftEdge.upper()); - if (upper.x < qMin(l.x, u.x)) - return true; - if (upper.x > qMax(l.x, u.x)) - return false; - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(upper, l, u); - // d < 0: left, d > 0: right, d == 0: on top - if (d == 0) - d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.lower()), l, u); - return d < 0; -} - -template -QRBTree::Node *QTriangulator::ComplexToSimple::searchEdgeLeftOf(int edgeIndex) const -{ - QRBTree::Node *current = m_edgeList.root; - QRBTree::Node *result = 0; - while (current) { - if (edgeIsLeftOfEdge(edgeIndex, current->data)) { - current = current->left; - } else { - result = current; - current = current->right; - } - } - return result; -} - -template -QRBTree::Node *QTriangulator::ComplexToSimple::searchEdgeLeftOf(int edgeIndex, QRBTree::Node *after) const -{ - if (!m_edgeList.root) - return after; - QRBTree::Node *result = after; - QRBTree::Node *current = (after ? m_edgeList.next(after) : m_edgeList.front(m_edgeList.root)); - while (current) { - if (edgeIsLeftOfEdge(edgeIndex, current->data)) - return result; - result = current; - current = m_edgeList.next(current); - } - return result; -} - -template -QPair::Node *, QRBTree::Node *> QTriangulator::ComplexToSimple::bounds(const QPodPoint &point) const -{ - QRBTree::Node *current = m_edgeList.root; - QPair::Node *, QRBTree::Node *> result(0, 0); - while (current) { - const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); - const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); - if (d == 0) { - result.first = result.second = current; - break; - } - current = (d < 0 ? current->left : current->right); - } - if (current == 0) - return result; - - current = result.first->left; - while (current) { - const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); - const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); - Q_ASSERT(d >= 0); - if (d == 0) { - result.first = current; - current = current->left; - } else { - current = current->right; - } - } - - current = result.second->right; - while (current) { - const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); - const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); - Q_ASSERT(d <= 0); - if (d == 0) { - result.second = current; - current = current->right; - } else { - current = current->left; - } - } - - return result; -} - -template -QPair::Node *, QRBTree::Node *> QTriangulator::ComplexToSimple::outerBounds(const QPodPoint &point) const -{ - QRBTree::Node *current = m_edgeList.root; - QPair::Node *, QRBTree::Node *> result(0, 0); - - while (current) { - const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); - const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); - if (d == 0) - break; - if (d < 0) { - result.second = current; - current = current->left; - } else { - result.first = current; - current = current->right; - } - } - - if (!current) - return result; - - QRBTree::Node *mid = current; - - current = mid->left; - while (current) { - const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); - const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); - Q_ASSERT(d >= 0); - if (d == 0) { - current = current->left; - } else { - result.first = current; - current = current->right; - } - } - - current = mid->right; - while (current) { - const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); - const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); - Q_ASSERT(d <= 0); - if (d == 0) { - current = current->right; - } else { - result.second = current; - current = current->left; - } - } - - return result; -} - -template -void QTriangulator::ComplexToSimple::splitEdgeListRange(QRBTree::Node *leftmost, QRBTree::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint) -{ - Q_ASSERT(leftmost && rightmost); - - // Split. - for (;;) { - const QPodPoint &u = m_parent->m_vertices.at(m_edges.at(leftmost->data).from); - const QPodPoint &v = m_parent->m_vertices.at(m_edges.at(leftmost->data).to); - Q_ASSERT(intersectionPoint.isOnLine(u, v)); - const Split split = {vertex, leftmost->data, intersectionPoint.isAccurate()}; - if (intersectionPoint.xOffset.numerator != 0 || intersectionPoint.yOffset.numerator != 0 || (intersectionPoint.upperLeft != u && intersectionPoint.upperLeft != v)) - m_splits.add(split); - if (leftmost == rightmost) - break; - leftmost = m_edgeList.next(leftmost); - } -} - -template -void QTriangulator::ComplexToSimple::reorderEdgeListRange(QRBTree::Node *leftmost, QRBTree::Node *rightmost) -{ - Q_ASSERT(leftmost && rightmost); - - QRBTree::Node *storeLeftmost = leftmost; - QRBTree::Node *storeRightmost = rightmost; - - // Reorder. - while (leftmost != rightmost) { - Edge &left = m_edges.at(leftmost->data); - Edge &right = m_edges.at(rightmost->data); - qSwap(left.node, right.node); - qSwap(leftmost->data, rightmost->data); - leftmost = m_edgeList.next(leftmost); - if (leftmost == rightmost) - break; - rightmost = m_edgeList.previous(rightmost); - } - - rightmost = m_edgeList.next(storeRightmost); - leftmost = m_edgeList.previous(storeLeftmost); - if (leftmost) - calculateIntersection(leftmost->data, storeLeftmost->data); - if (rightmost) - calculateIntersection(storeRightmost->data, rightmost->data); -} - -template -void QTriangulator::ComplexToSimple::sortEdgeList(const QPodPoint eventPoint) -{ - QIntersectionPoint eventPoint2 = QT_PREPEND_NAMESPACE(qIntersectionPoint)(eventPoint); - while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint < eventPoint2) { - Intersection intersection = m_topIntersection.pop(); - - QIntersectionPoint currentIntersectionPoint = intersection.intersectionPoint; - int currentVertex = intersection.vertex; - - QRBTree::Node *leftmost = m_edges.at(intersection.leftEdge).node; - QRBTree::Node *rightmost = m_edges.at(intersection.rightEdge).node; - - for (;;) { - QRBTree::Node *previous = m_edgeList.previous(leftmost); - if (!previous) - break; - const Edge &edge = m_edges.at(previous->data); - const QPodPoint &u = m_parent->m_vertices.at((qint32)edge.from); - const QPodPoint &v = m_parent->m_vertices.at((qint32)edge.to); - if (!currentIntersectionPoint.isOnLine(u, v)) { - Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0); - break; - } - leftmost = previous; - } - - for (;;) { - QRBTree::Node *next = m_edgeList.next(rightmost); - if (!next) - break; - const Edge &edge = m_edges.at(next->data); - const QPodPoint &u = m_parent->m_vertices.at((qint32)edge.from); - const QPodPoint &v = m_parent->m_vertices.at((qint32)edge.to); - if (!currentIntersectionPoint.isOnLine(u, v)) { - Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0); - break; - } - rightmost = next; - } - - Q_ASSERT(leftmost && rightmost); - splitEdgeListRange(leftmost, rightmost, currentVertex, currentIntersectionPoint); - reorderEdgeListRange(leftmost, rightmost); - - while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= currentIntersectionPoint) - m_topIntersection.pop(); - -#ifdef Q_TRIANGULATOR_DEBUG - DebugDialog dialog(this, intersection.vertex); - dialog.exec(); -#endif - - } -} - -template -void QTriangulator::ComplexToSimple::fillPriorityQueue() -{ - m_events.reset(); - m_events.reserve(m_edges.size() * 2); - for (int i = 0; i < m_edges.size(); ++i) { - Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1); - Q_ASSERT(m_edges.at(i).node == 0); - Q_ASSERT(m_edges.at(i).pointingUp == m_edges.at(i).originallyPointingUp); - Q_ASSERT(m_edges.at(i).pointingUp == (m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from))); - // Ignore zero-length edges. - if (m_parent->m_vertices.at(m_edges.at(i).to) != m_parent->m_vertices.at(m_edges.at(i).from)) { - QPodPoint upper = m_parent->m_vertices.at(m_edges.at(i).upper()); - QPodPoint lower = m_parent->m_vertices.at(m_edges.at(i).lower()); - Event upperEvent = {{upper.x, upper.y}, Event::Upper, i}; - Event lowerEvent = {{lower.x, lower.y}, Event::Lower, i}; - m_events.add(upperEvent); - m_events.add(lowerEvent); - } - } - - std::sort(m_events.data(), m_events.data() + m_events.size()); -} - -template -void QTriangulator::ComplexToSimple::calculateIntersections() -{ - fillPriorityQueue(); - - Q_ASSERT(m_topIntersection.empty()); - Q_ASSERT(m_edgeList.root == 0); - - // Find all intersection points. - while (!m_events.isEmpty()) { - Event event = m_events.last(); - sortEdgeList(event.point); - - // Find all edges in the edge list that contain the current vertex and mark them to be split later. - QPair::Node *, QRBTree::Node *> range = bounds(event.point); - QRBTree::Node *leftNode = range.first ? m_edgeList.previous(range.first) : 0; - int vertex = (event.type == Event::Upper ? m_edges.at(event.edge).upper() : m_edges.at(event.edge).lower()); - QIntersectionPoint eventPoint = QT_PREPEND_NAMESPACE(qIntersectionPoint)(event.point); - - if (range.first != 0) { - splitEdgeListRange(range.first, range.second, vertex, eventPoint); - reorderEdgeListRange(range.first, range.second); - } - - // Handle the edges with start or end point in the current vertex. - while (!m_events.isEmpty() && m_events.last().point == event.point) { - event = m_events.last(); - m_events.pop_back(); - int i = event.edge; - - if (m_edges.at(i).node) { - // Remove edge from edge list. - Q_ASSERT(event.type == Event::Lower); - QRBTree::Node *left = m_edgeList.previous(m_edges.at(i).node); - QRBTree::Node *right = m_edgeList.next(m_edges.at(i).node); - m_edgeList.deleteNode(m_edges.at(i).node); - if (!left || !right) - continue; - calculateIntersection(left->data, right->data); - } else { - // Insert edge into edge list. - Q_ASSERT(event.type == Event::Upper); - QRBTree::Node *left = searchEdgeLeftOf(i, leftNode); - m_edgeList.attachAfter(left, m_edges.at(i).node = m_edgeList.newNode()); - m_edges.at(i).node->data = i; - QRBTree::Node *right = m_edgeList.next(m_edges.at(i).node); - if (left) - calculateIntersection(left->data, i); - if (right) - calculateIntersection(i, right->data); - } - } - while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= eventPoint) - m_topIntersection.pop(); -#ifdef Q_TRIANGULATOR_DEBUG - DebugDialog dialog(this, vertex); - dialog.exec(); -#endif - } - m_processedEdgePairs.clear(); -} - -// Split an edge into two pieces at the given point. -// The upper piece is pushed to the end of the 'm_edges' vector. -// The lower piece replaces the old edge. -// Return the edge whose 'from' is 'pointIndex'. -template -int QTriangulator::ComplexToSimple::splitEdge(int splitIndex) -{ - const Split &split = m_splits.at(splitIndex); - Edge &lowerEdge = m_edges.at(split.edge); - Q_ASSERT(lowerEdge.node == 0); - Q_ASSERT(lowerEdge.previous == -1 && lowerEdge.next == -1); - - if (lowerEdge.from == split.vertex) - return split.edge; - if (lowerEdge.to == split.vertex) - return lowerEdge.next; - - // Check that angle >= 90 degrees. - //Q_ASSERT(qDot(m_points.at(m_edges.at(edgeIndex).from) - m_points.at(pointIndex), - // m_points.at(m_edges.at(edgeIndex).to) - m_points.at(pointIndex)) <= 0); - - Edge upperEdge = lowerEdge; - upperEdge.mayIntersect |= !split.accurate; // The edge may have been split before at an inaccurate split point. - lowerEdge.mayIntersect = !split.accurate; - if (lowerEdge.pointingUp) { - lowerEdge.to = upperEdge.from = split.vertex; - m_edges.add(upperEdge); - return m_edges.size() - 1; - } else { - lowerEdge.from = upperEdge.to = split.vertex; - m_edges.add(upperEdge); - return split.edge; - } -} - -template -bool QTriangulator::ComplexToSimple::splitEdgesAtIntersections() -{ - for (int i = 0; i < m_edges.size(); ++i) - m_edges.at(i).mayIntersect = false; - bool checkForNewIntersections = false; - for (int i = 0; i < m_splits.size(); ++i) { - splitEdge(i); - checkForNewIntersections |= !m_splits.at(i).accurate; - } - for (int i = 0; i < m_edges.size(); ++i) { - m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp = - m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); - } - m_splits.reset(); - return checkForNewIntersections; -} - -template -void QTriangulator::ComplexToSimple::insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i) -{ - // Edges with zero length should not reach this part. - Q_ASSERT(m_parent->m_vertices.at(m_edges.at(i).from) != m_parent->m_vertices.at(m_edges.at(i).to)); - - // Skip edges with unwanted winding number. - int windingNumber = m_edges.at(i).winding; - if (m_edges.at(i).originallyPointingUp) - ++windingNumber; - - // Make sure exactly one fill rule is specified. - Q_ASSERT(((m_parent->m_hint & QVectorPath::WindingFill) != 0) != ((m_parent->m_hint & QVectorPath::OddEvenFill) != 0)); - - if ((m_parent->m_hint & QVectorPath::WindingFill) && windingNumber != 0 && windingNumber != 1) - return; - - // Skip cancelling edges. - if (!orderedEdges.isEmpty()) { - int j = orderedEdges[orderedEdges.size() - 1]; - // If the last edge is already connected in one end, it should not be cancelled. - if (m_edges.at(j).next == -1 && m_edges.at(j).previous == -1 - && (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(j).to)) - && (m_parent->m_vertices.at(m_edges.at(i).to) == m_parent->m_vertices.at(m_edges.at(j).from))) { - orderedEdges.removeLast(); - return; - } - } - orderedEdges.append(i); -} - -template -void QTriangulator::ComplexToSimple::removeUnwantedEdgesAndConnect() -{ - Q_ASSERT(m_edgeList.root == 0); - // Initialize priority queue. - fillPriorityQueue(); - - ShortArray orderedEdges; - - while (!m_events.isEmpty()) { - Event event = m_events.last(); - int edgeIndex = event.edge; - - // Check that all the edges in the list crosses the current scanline - //if (m_edgeList.root) { - // for (QRBTree::Node *node = m_edgeList.front(m_edgeList.root); node; node = m_edgeList.next(node)) { - // Q_ASSERT(event.point <= m_points.at(m_edges.at(node->data).lower())); - // } - //} - - orderedEdges.clear(); - QPair::Node *, QRBTree::Node *> b = outerBounds(event.point); - if (m_edgeList.root) { - QRBTree::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root)); - // Process edges that are going to be removed from the edge list at the current event point. - while (current != b.second) { - Q_ASSERT(current); - Q_ASSERT(m_edges.at(current->data).node == current); - Q_ASSERT(QT_PREPEND_NAMESPACE(qIntersectionPoint)(event.point).isOnLine(m_parent->m_vertices.at(m_edges.at(current->data).from), m_parent->m_vertices.at(m_edges.at(current->data).to))); - Q_ASSERT(m_parent->m_vertices.at(m_edges.at(current->data).from) == event.point || m_parent->m_vertices.at(m_edges.at(current->data).to) == event.point); - insertEdgeIntoVectorIfWanted(orderedEdges, current->data); - current = m_edgeList.next(current); - } - } - - // Remove edges above the event point, insert edges below the event point. - do { - event = m_events.last(); - m_events.pop_back(); - edgeIndex = event.edge; - - // Edges with zero length should not reach this part. - Q_ASSERT(m_parent->m_vertices.at(m_edges.at(edgeIndex).from) != m_parent->m_vertices.at(m_edges.at(edgeIndex).to)); - - if (m_edges.at(edgeIndex).node) { - Q_ASSERT(event.type == Event::Lower); - Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).lower())); - m_edgeList.deleteNode(m_edges.at(edgeIndex).node); - } else { - Q_ASSERT(event.type == Event::Upper); - Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).upper())); - QRBTree::Node *left = searchEdgeLeftOf(edgeIndex, b.first); - m_edgeList.attachAfter(left, m_edges.at(edgeIndex).node = m_edgeList.newNode()); - m_edges.at(edgeIndex).node->data = edgeIndex; - } - } while (!m_events.isEmpty() && m_events.last().point == event.point); - - if (m_edgeList.root) { - QRBTree::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root)); - - // Calculate winding number and turn counter-clockwise. - int currentWindingNumber = (b.first ? m_edges.at(b.first->data).winding : 0); - while (current != b.second) { - Q_ASSERT(current); - //Q_ASSERT(b.second == 0 || m_edgeList.order(current, b.second) < 0); - int i = current->data; - Q_ASSERT(m_edges.at(i).node == current); - - // Winding number. - int ccwWindingNumber = m_edges.at(i).winding = currentWindingNumber; - if (m_edges.at(i).originallyPointingUp) { - --m_edges.at(i).winding; - } else { - ++m_edges.at(i).winding; - ++ccwWindingNumber; - } - currentWindingNumber = m_edges.at(i).winding; - - // Turn counter-clockwise. - if ((ccwWindingNumber & 1) == 0) { - Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1); - qSwap(m_edges.at(i).from, m_edges.at(i).to); - m_edges.at(i).pointingUp = !m_edges.at(i).pointingUp; - } - - current = m_edgeList.next(current); - } - - // Process edges that were inserted into the edge list at the current event point. - current = (b.second ? m_edgeList.previous(b.second) : m_edgeList.back(m_edgeList.root)); - while (current != b.first) { - Q_ASSERT(current); - Q_ASSERT(m_edges.at(current->data).node == current); - insertEdgeIntoVectorIfWanted(orderedEdges, current->data); - current = m_edgeList.previous(current); - } - } - if (orderedEdges.isEmpty()) - continue; - - Q_ASSERT((orderedEdges.size() & 1) == 0); - - // Connect edges. - // First make sure the first edge point towards the current point. - int i; - if (m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).from) == event.point) { - i = 1; - int copy = orderedEdges[0]; // Make copy in case the append() will cause a reallocation. - orderedEdges.append(copy); - } else { - Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).to) == event.point); - i = 0; - } - - // Remove references to duplicate points. First find the point with lowest index. - int pointIndex = INT_MAX; - for (int j = i; j < orderedEdges.size(); j += 2) { - Q_ASSERT(j + 1 < orderedEdges.size()); - Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j]).to) == event.point); - Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j + 1]).from) == event.point); - if (m_edges.at(orderedEdges[j]).to < pointIndex) - pointIndex = m_edges.at(orderedEdges[j]).to; - if (m_edges.at(orderedEdges[j + 1]).from < pointIndex) - pointIndex = m_edges.at(orderedEdges[j + 1]).from; - } - - for (; i < orderedEdges.size(); i += 2) { - // Remove references to duplicate points by making all edges reference one common point. - m_edges.at(orderedEdges[i]).to = m_edges.at(orderedEdges[i + 1]).from = pointIndex; - - Q_ASSERT(m_edges.at(orderedEdges[i]).pointingUp || m_edges.at(orderedEdges[i]).previous != -1); - Q_ASSERT(!m_edges.at(orderedEdges[i + 1]).pointingUp || m_edges.at(orderedEdges[i + 1]).next != -1); - - m_edges.at(orderedEdges[i]).next = orderedEdges[i + 1]; - m_edges.at(orderedEdges[i + 1]).previous = orderedEdges[i]; - } - } // end while -} - -template -void QTriangulator::ComplexToSimple::removeUnusedPoints() { - QBitArray used(m_parent->m_vertices.size(), false); - for (int i = 0; i < m_edges.size(); ++i) { - Q_ASSERT((m_edges.at(i).previous == -1) == (m_edges.at(i).next == -1)); - if (m_edges.at(i).next != -1) - used.setBit(m_edges.at(i).from); - } - QDataBuffer newMapping(m_parent->m_vertices.size()); - newMapping.resize(m_parent->m_vertices.size()); - int count = 0; - for (int i = 0; i < m_parent->m_vertices.size(); ++i) { - if (used.at(i)) { - m_parent->m_vertices.at(count) = m_parent->m_vertices.at(i); - newMapping.at(i) = count; - ++count; - } - } - m_parent->m_vertices.resize(count); - for (int i = 0; i < m_edges.size(); ++i) { - m_edges.at(i).from = newMapping.at(m_edges.at(i).from); - m_edges.at(i).to = newMapping.at(m_edges.at(i).to); - } -} - -template -inline bool QTriangulator::ComplexToSimple::Event::operator < (const Event &other) const -{ - if (point == other.point) - return type < other.type; // 'Lower' has higher priority than 'Upper'. - return other.point < point; -} - -//============================================================================// -// QTriangulator::ComplexToSimple::DebugDialog // -//============================================================================// - -#ifdef Q_TRIANGULATOR_DEBUG -template -QTriangulator::ComplexToSimple::DebugDialog::DebugDialog(ComplexToSimple *parent, int currentVertex) - : m_parent(parent), m_vertex(currentVertex) -{ - QDataBuffer &vertices = m_parent->m_parent->m_vertices; - if (vertices.isEmpty()) - return; - - int minX, maxX, minY, maxY; - minX = maxX = vertices.at(0).x; - minY = maxY = vertices.at(0).y; - for (int i = 1; i < vertices.size(); ++i) { - minX = qMin(minX, vertices.at(i).x); - maxX = qMax(maxX, vertices.at(i).x); - minY = qMin(minY, vertices.at(i).y); - maxY = qMax(maxY, vertices.at(i).y); - } - int w = maxX - minX; - int h = maxY - minY; - qreal border = qMin(w, h) / 10.0; - m_window = QRectF(minX - border, minY - border, (maxX - minX + 2 * border), (maxY - minY + 2 * border)); -} - -template -void QTriangulator::ComplexToSimple::DebugDialog::paintEvent(QPaintEvent *) -{ - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing, true); - p.fillRect(rect(), Qt::black); - QDataBuffer &vertices = m_parent->m_parent->m_vertices; - if (vertices.isEmpty()) - return; - - qreal halfPointSize = qMin(m_window.width(), m_window.height()) / 300.0; - p.setWindow(m_window.toRect()); - - p.setPen(Qt::white); - - QDataBuffer &edges = m_parent->m_edges; - for (int i = 0; i < edges.size(); ++i) { - QPodPoint u = vertices.at(edges.at(i).from); - QPodPoint v = vertices.at(edges.at(i).to); - p.drawLine(u.x, u.y, v.x, v.y); - } - - for (int i = 0; i < vertices.size(); ++i) { - QPodPoint q = vertices.at(i); - p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::red); - } - - Qt::GlobalColor colors[6] = {Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow}; - p.setOpacity(0.5); - int count = 0; - if (m_parent->m_edgeList.root) { - QRBTree::Node *current = m_parent->m_edgeList.front(m_parent->m_edgeList.root); - while (current) { - p.setPen(colors[count++ % 6]); - QPodPoint u = vertices.at(edges.at(current->data).from); - QPodPoint v = vertices.at(edges.at(current->data).to); - p.drawLine(u.x, u.y, v.x, v.y); - current = m_parent->m_edgeList.next(current); - } - } - - p.setOpacity(1.0); - QPodPoint q = vertices.at(m_vertex); - p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::green); - - p.setPen(Qt::gray); - QDataBuffer &splits = m_parent->m_splits; - for (int i = 0; i < splits.size(); ++i) { - QPodPoint q = vertices.at(splits.at(i).vertex); - QPodPoint u = vertices.at(edges.at(splits.at(i).edge).from) - q; - QPodPoint v = vertices.at(edges.at(splits.at(i).edge).to) - q; - qreal uLen = qSqrt(qDot(u, u)); - qreal vLen = qSqrt(qDot(v, v)); - if (uLen) { - u.x *= 2 * halfPointSize / uLen; - u.y *= 2 * halfPointSize / uLen; - } - if (vLen) { - v.x *= 2 * halfPointSize / vLen; - v.y *= 2 * halfPointSize / vLen; - } - u += q; - v += q; - p.drawLine(u.x, u.y, v.x, v.y); - } -} - -template -void QTriangulator::ComplexToSimple::DebugDialog::wheelEvent(QWheelEvent *event) -{ - qreal scale = qExp(-0.001 * event->delta()); - QPointF center = m_window.center(); - QPointF delta = scale * (m_window.bottomRight() - center); - m_window = QRectF(center - delta, center + delta); - event->accept(); - update(); -} - -template -void QTriangulator::ComplexToSimple::DebugDialog::mouseMoveEvent(QMouseEvent *event) -{ - if (event->buttons() & Qt::LeftButton) { - QPointF delta = event->pos() - m_lastMousePos; - delta.setX(delta.x() * m_window.width() / width()); - delta.setY(delta.y() * m_window.height() / height()); - m_window.translate(-delta.x(), -delta.y()); - m_lastMousePos = event->pos(); - event->accept(); - update(); - } -} - -template -void QTriangulator::ComplexToSimple::DebugDialog::mousePressEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton) - m_lastMousePos = event->pos(); - event->accept(); -} - - -#endif - -//============================================================================// -// QTriangulator::SimpleToMonotone // -//============================================================================// -template -void QTriangulator::SimpleToMonotone::decompose() -{ - setupDataStructures(); - removeZeroLengthEdges(); - monotoneDecomposition(); - - m_parent->m_indices.clear(); - QBitArray processed(m_edges.size(), false); - for (int first = 0; first < m_edges.size(); ++first) { - if (processed.at(first)) - continue; - int i = first; - do { - Q_ASSERT(!processed.at(i)); - Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i); - m_parent->m_indices.push_back(m_edges.at(i).from); - processed.setBit(i); - i = m_edges.at(i).next; - } while (i != first); - if (m_parent->m_indices.size() > 0 && m_parent->m_indices.back() != T(-1)) // Q_TRIANGULATE_END_OF_POLYGON - m_parent->m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON - } -} - -template -void QTriangulator::SimpleToMonotone::setupDataStructures() -{ - int i = 0; - Edge e; - e.node = 0; - e.twin = -1; - - while (i + 3 <= m_parent->m_indices.size()) { - int start = m_edges.size(); - - do { - e.from = m_parent->m_indices.at(i); - e.type = RegularVertex; - e.next = m_edges.size() + 1; - e.previous = m_edges.size() - 1; - m_edges.add(e); - ++i; - Q_ASSERT(i < m_parent->m_indices.size()); - } while (m_parent->m_indices.at(i) != T(-1)); // Q_TRIANGULATE_END_OF_POLYGON - - m_edges.last().next = start; - m_edges.at(start).previous = m_edges.size() - 1; - ++i; // Skip Q_TRIANGULATE_END_OF_POLYGON. - } - - for (i = 0; i < m_edges.size(); ++i) { - m_edges.at(i).to = m_edges.at(m_edges.at(i).next).from; - m_edges.at(i).pointingUp = m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); - m_edges.at(i).helper = -1; // Not initialized here. - } -} - -template -void QTriangulator::SimpleToMonotone::removeZeroLengthEdges() -{ - for (int i = 0; i < m_edges.size(); ++i) { - if (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(i).to)) { - m_edges.at(m_edges.at(i).previous).next = m_edges.at(i).next; - m_edges.at(m_edges.at(i).next).previous = m_edges.at(i).previous; - m_edges.at(m_edges.at(i).next).from = m_edges.at(i).from; - m_edges.at(i).next = -1; // Mark as removed. - } - } - - QDataBuffer newMapping(m_edges.size()); - newMapping.resize(m_edges.size()); - int count = 0; - for (int i = 0; i < m_edges.size(); ++i) { - if (m_edges.at(i).next != -1) { - m_edges.at(count) = m_edges.at(i); - newMapping.at(i) = count; - ++count; - } - } - m_edges.resize(count); - for (int i = 0; i < m_edges.size(); ++i) { - m_edges.at(i).next = newMapping.at(m_edges.at(i).next); - m_edges.at(i).previous = newMapping.at(m_edges.at(i).previous); - } -} - -template -void QTriangulator::SimpleToMonotone::fillPriorityQueue() -{ - m_upperVertex.reset(); - m_upperVertex.reserve(m_edges.size()); - for (int i = 0; i < m_edges.size(); ++i) - m_upperVertex.add(i); - CompareVertices cmp(this); - std::sort(m_upperVertex.data(), m_upperVertex.data() + m_upperVertex.size(), cmp); - //for (int i = 1; i < m_upperVertex.size(); ++i) { - // Q_ASSERT(!cmp(m_upperVertex.at(i), m_upperVertex.at(i - 1))); - //} -} - -template -bool QTriangulator::SimpleToMonotone::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const -{ - const Edge &leftEdge = m_edges.at(leftEdgeIndex); - const Edge &rightEdge = m_edges.at(rightEdgeIndex); - const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper()); - const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower()); - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.upper()), l, u); - // d < 0: left, d > 0: right, d == 0: on top - if (d == 0) - d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.lower()), l, u); - return d < 0; -} - -// Returns the rightmost edge not to the right of the given edge. -template -QRBTree::Node *QTriangulator::SimpleToMonotone::searchEdgeLeftOfEdge(int edgeIndex) const -{ - QRBTree::Node *current = m_edgeList.root; - QRBTree::Node *result = 0; - while (current) { - if (edgeIsLeftOfEdge(edgeIndex, current->data)) { - current = current->left; - } else { - result = current; - current = current->right; - } - } - return result; -} - -// Returns the rightmost edge left of the given point. -template -QRBTree::Node *QTriangulator::SimpleToMonotone::searchEdgeLeftOfPoint(int pointIndex) const -{ - QRBTree::Node *current = m_edgeList.root; - QRBTree::Node *result = 0; - while (current) { - const QPodPoint &p1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); - const QPodPoint &p2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(pointIndex), p1, p2); - if (d <= 0) { - current = current->left; - } else { - result = current; - current = current->right; - } - } - return result; -} - -template -void QTriangulator::SimpleToMonotone::classifyVertex(int i) -{ - Edge &e2 = m_edges.at(i); - const Edge &e1 = m_edges.at(e2.previous); - - bool startOrSplit = (e1.pointingUp && !e2.pointingUp); - bool endOrMerge = (!e1.pointingUp && e2.pointingUp); - - const QPodPoint &p1 = m_parent->m_vertices.at(e1.from); - const QPodPoint &p2 = m_parent->m_vertices.at(e2.from); - const QPodPoint &p3 = m_parent->m_vertices.at(e2.to); - qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(p1, p2, p3); - Q_ASSERT(d != 0 || (!startOrSplit && !endOrMerge)); - - e2.type = RegularVertex; - - if (m_clockwiseOrder) { - if (startOrSplit) - e2.type = (d < 0 ? SplitVertex : StartVertex); - else if (endOrMerge) - e2.type = (d < 0 ? MergeVertex : EndVertex); - } else { - if (startOrSplit) - e2.type = (d > 0 ? SplitVertex : StartVertex); - else if (endOrMerge) - e2.type = (d > 0 ? MergeVertex : EndVertex); - } -} - -template -void QTriangulator::SimpleToMonotone::classifyVertices() -{ - for (int i = 0; i < m_edges.size(); ++i) - classifyVertex(i); -} - -template -bool QTriangulator::SimpleToMonotone::pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3) -{ - bool leftOfPreviousEdge = !qPointIsLeftOfLine(p, v2, v1); - bool leftOfNextEdge = !qPointIsLeftOfLine(p, v3, v2); - - if (qPointIsLeftOfLine(v1, v2, v3)) - return leftOfPreviousEdge && leftOfNextEdge; - else - return leftOfPreviousEdge || leftOfNextEdge; -} - -template -bool QTriangulator::SimpleToMonotone::pointIsInSector(int vertex, int sector) -{ - const QPodPoint ¢er = m_parent->m_vertices.at(m_edges.at(sector).from); - // Handle degenerate edges. - while (m_parent->m_vertices.at(m_edges.at(vertex).from) == center) - vertex = m_edges.at(vertex).next; - int next = m_edges.at(sector).next; - while (m_parent->m_vertices.at(m_edges.at(next).from) == center) - next = m_edges.at(next).next; - int previous = m_edges.at(sector).previous; - while (m_parent->m_vertices.at(m_edges.at(previous).from) == center) - previous = m_edges.at(previous).previous; - - const QPodPoint &p = m_parent->m_vertices.at(m_edges.at(vertex).from); - const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(previous).from); - const QPodPoint &v3 = m_parent->m_vertices.at(m_edges.at(next).from); - if (m_clockwiseOrder) - return pointIsInSector(p, v3, center, v1); - else - return pointIsInSector(p, v1, center, v3); -} - -template -int QTriangulator::SimpleToMonotone::findSector(int edge, int vertex) -{ - while (!pointIsInSector(vertex, edge)) { - edge = m_edges.at(m_edges.at(edge).previous).twin; - Q_ASSERT(edge != -1); - } - return edge; -} - -template -void QTriangulator::SimpleToMonotone::createDiagonal(int lower, int upper) -{ - lower = findSector(lower, upper); - upper = findSector(upper, lower); - - int prevLower = m_edges.at(lower).previous; - int prevUpper = m_edges.at(upper).previous; - - Edge e; - - e.twin = m_edges.size() + 1; - e.next = upper; - e.previous = prevLower; - e.from = m_edges.at(lower).from; - e.to = m_edges.at(upper).from; - m_edges.at(upper).previous = m_edges.at(prevLower).next = int(m_edges.size()); - m_edges.add(e); - - e.twin = m_edges.size() - 1; - e.next = lower; - e.previous = prevUpper; - e.from = m_edges.at(upper).from; - e.to = m_edges.at(lower).from; - m_edges.at(lower).previous = m_edges.at(prevUpper).next = int(m_edges.size()); - m_edges.add(e); -} - -template -void QTriangulator::SimpleToMonotone::monotoneDecomposition() -{ - if (m_edges.isEmpty()) - return; - - Q_ASSERT(!m_edgeList.root); - QDataBuffer > diagonals(m_upperVertex.size()); - - int i = 0; - for (int index = 1; index < m_edges.size(); ++index) { - if (m_parent->m_vertices.at(m_edges.at(index).from) < m_parent->m_vertices.at(m_edges.at(i).from)) - i = index; - } - Q_ASSERT(i < m_edges.size()); - int j = m_edges.at(i).previous; - Q_ASSERT(j < m_edges.size()); - m_clockwiseOrder = qPointIsLeftOfLine(m_parent->m_vertices.at((quint32)m_edges.at(i).from), - m_parent->m_vertices.at((quint32)m_edges.at(j).from), m_parent->m_vertices.at((quint32)m_edges.at(i).to)); - - classifyVertices(); - fillPriorityQueue(); - - // debug: set helpers explicitly (shouldn't be necessary) - //for (int i = 0; i < m_edges.size(); ++i) - // m_edges.at(i).helper = m_edges.at(i).upper(); - - while (!m_upperVertex.isEmpty()) { - i = m_upperVertex.last(); - Q_ASSERT(i < m_edges.size()); - m_upperVertex.pop_back(); - j = m_edges.at(i).previous; - Q_ASSERT(j < m_edges.size()); - - QRBTree::Node *leftEdgeNode = 0; - - switch (m_edges.at(i).type) { - case RegularVertex: - // If polygon interior is to the right of the vertex... - if (m_edges.at(i).pointingUp == m_clockwiseOrder) { - if (m_edges.at(i).node) { - Q_ASSERT(!m_edges.at(j).node); - if (m_edges.at(m_edges.at(i).helper).type == MergeVertex) - diagonals.add(QPair(i, m_edges.at(i).helper)); - m_edges.at(j).node = m_edges.at(i).node; - m_edges.at(i).node = 0; - m_edges.at(j).node->data = j; - m_edges.at(j).helper = i; - } else if (m_edges.at(j).node) { - Q_ASSERT(!m_edges.at(i).node); - if (m_edges.at(m_edges.at(j).helper).type == MergeVertex) - diagonals.add(QPair(i, m_edges.at(j).helper)); - m_edges.at(i).node = m_edges.at(j).node; - m_edges.at(j).node = 0; - m_edges.at(i).node->data = i; - m_edges.at(i).helper = i; - } else { - qWarning("Inconsistent polygon. (#1)"); - } - } else { - leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); - if (leftEdgeNode) { - if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex) - diagonals.add(QPair(i, m_edges.at(leftEdgeNode->data).helper)); - m_edges.at(leftEdgeNode->data).helper = i; - } else { - qWarning("Inconsistent polygon. (#2)"); - } - } - break; - case SplitVertex: - leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); - if (leftEdgeNode) { - diagonals.add(QPair(i, m_edges.at(leftEdgeNode->data).helper)); - m_edges.at(leftEdgeNode->data).helper = i; - } else { - qWarning("Inconsistent polygon. (#3)"); - } - // Fall through. - case StartVertex: - if (m_clockwiseOrder) { - leftEdgeNode = searchEdgeLeftOfEdge(j); - QRBTree::Node *node = m_edgeList.newNode(); - node->data = j; - m_edges.at(j).node = node; - m_edges.at(j).helper = i; - m_edgeList.attachAfter(leftEdgeNode, node); - Q_ASSERT(m_edgeList.validate()); - } else { - leftEdgeNode = searchEdgeLeftOfEdge(i); - QRBTree::Node *node = m_edgeList.newNode(); - node->data = i; - m_edges.at(i).node = node; - m_edges.at(i).helper = i; - m_edgeList.attachAfter(leftEdgeNode, node); - Q_ASSERT(m_edgeList.validate()); - } - break; - case MergeVertex: - leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); - if (leftEdgeNode) { - if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex) - diagonals.add(QPair(i, m_edges.at(leftEdgeNode->data).helper)); - m_edges.at(leftEdgeNode->data).helper = i; - } else { - qWarning("Inconsistent polygon. (#4)"); - } - // Fall through. - case EndVertex: - if (m_clockwiseOrder) { - if (m_edges.at(m_edges.at(i).helper).type == MergeVertex) - diagonals.add(QPair(i, m_edges.at(i).helper)); - if (m_edges.at(i).node) { - m_edgeList.deleteNode(m_edges.at(i).node); - Q_ASSERT(m_edgeList.validate()); - } else { - qWarning("Inconsistent polygon. (#5)"); - } - } else { - if (m_edges.at(m_edges.at(j).helper).type == MergeVertex) - diagonals.add(QPair(i, m_edges.at(j).helper)); - if (m_edges.at(j).node) { - m_edgeList.deleteNode(m_edges.at(j).node); - Q_ASSERT(m_edgeList.validate()); - } else { - qWarning("Inconsistent polygon. (#6)"); - } - } - break; - } - } - - for (int i = 0; i < diagonals.size(); ++i) - createDiagonal(diagonals.at(i).first, diagonals.at(i).second); -} - -template -bool QTriangulator::SimpleToMonotone::CompareVertices::operator () (int i, int j) const -{ - if (m_parent->m_edges.at(i).from == m_parent->m_edges.at(j).from) - return m_parent->m_edges.at(i).type > m_parent->m_edges.at(j).type; - return m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).from) > - m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).from); -} - -//============================================================================// -// QTriangulator::MonotoneToTriangles // -//============================================================================// -template -void QTriangulator::MonotoneToTriangles::decompose() -{ - QVector result; - QDataBuffer stack(m_parent->m_indices.size()); - m_first = 0; - // Require at least three more indices. - while (m_first + 3 <= m_parent->m_indices.size()) { - m_length = 0; - while (m_parent->m_indices.at(m_first + m_length) != T(-1)) { // Q_TRIANGULATE_END_OF_POLYGON - ++m_length; - Q_ASSERT(m_first + m_length < m_parent->m_indices.size()); - } - if (m_length < 3) { - m_first += m_length + 1; - continue; - } - - int minimum = 0; - while (less(next(minimum), minimum)) - minimum = next(minimum); - while (less(previous(minimum), minimum)) - minimum = previous(minimum); - - stack.reset(); - stack.add(minimum); - int left = previous(minimum); - int right = next(minimum); - bool stackIsOnLeftSide; - bool clockwiseOrder = leftOfEdge(minimum, left, right); - - if (less(left, right)) { - stack.add(left); - left = previous(left); - stackIsOnLeftSide = true; - } else { - stack.add(right); - right = next(right); - stackIsOnLeftSide = false; - } - - for (int count = 0; count + 2 < m_length; ++count) - { - Q_ASSERT(stack.size() >= 2); - if (less(left, right)) { - if (stackIsOnLeftSide == false) { - for (int i = 0; i + 1 < stack.size(); ++i) { - result.push_back(indices(stack.at(i + 1))); - result.push_back(indices(left)); - result.push_back(indices(stack.at(i))); - } - stack.first() = stack.last(); - stack.resize(1); - } else { - while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(left, stack.at(stack.size() - 2), stack.last()))) { - result.push_back(indices(stack.at(stack.size() - 2))); - result.push_back(indices(left)); - result.push_back(indices(stack.last())); - stack.pop_back(); - } - } - stack.add(left); - left = previous(left); - stackIsOnLeftSide = true; - } else { - if (stackIsOnLeftSide == true) { - for (int i = 0; i + 1 < stack.size(); ++i) { - result.push_back(indices(stack.at(i))); - result.push_back(indices(right)); - result.push_back(indices(stack.at(i + 1))); - } - stack.first() = stack.last(); - stack.resize(1); - } else { - while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(right, stack.last(), stack.at(stack.size() - 2)))) { - result.push_back(indices(stack.last())); - result.push_back(indices(right)); - result.push_back(indices(stack.at(stack.size() - 2))); - stack.pop_back(); - } - } - stack.add(right); - right = next(right); - stackIsOnLeftSide = false; - } - } - - m_first += m_length + 1; - } - m_parent->m_indices = result; -} - -//============================================================================// -// qTriangulate // -//============================================================================// - -static bool hasElementIndexUint() -{ - QOpenGLContext *context = QOpenGLContext::currentContext(); - if (!context) - return false; - return static_cast(context->functions())->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint); -} - -Q_GUI_EXPORT QTriangleSet qTriangulate(const qreal *polygon, - int count, uint hint, const QTransform &matrix) -{ - QTriangleSet triangleSet; - if (hasElementIndexUint()) { - QTriangulator triangulator; - triangulator.initialize(polygon, count, hint, matrix); - QVertexSet vertexSet = triangulator.triangulate(); - triangleSet.vertices = vertexSet.vertices; - triangleSet.indices.setDataUint(vertexSet.indices); - - } else { - QTriangulator triangulator; - triangulator.initialize(polygon, count, hint, matrix); - QVertexSet vertexSet = triangulator.triangulate(); - triangleSet.vertices = vertexSet.vertices; - triangleSet.indices.setDataUshort(vertexSet.indices); - } - return triangleSet; -} - -Q_GUI_EXPORT QTriangleSet qTriangulate(const QVectorPath &path, - const QTransform &matrix, qreal lod) -{ - QTriangleSet triangleSet; - if (hasElementIndexUint()) { - QTriangulator triangulator; - triangulator.initialize(path, matrix, lod); - QVertexSet vertexSet = triangulator.triangulate(); - triangleSet.vertices = vertexSet.vertices; - triangleSet.indices.setDataUint(vertexSet.indices); - } else { - QTriangulator triangulator; - triangulator.initialize(path, matrix, lod); - QVertexSet vertexSet = triangulator.triangulate(); - triangleSet.vertices = vertexSet.vertices; - triangleSet.indices.setDataUshort(vertexSet.indices); - } - return triangleSet; -} - -QTriangleSet qTriangulate(const QPainterPath &path, - const QTransform &matrix, qreal lod) -{ - QTriangleSet triangleSet; - if (hasElementIndexUint()) { - QTriangulator triangulator; - triangulator.initialize(path, matrix, lod); - QVertexSet vertexSet = triangulator.triangulate(); - triangleSet.vertices = vertexSet.vertices; - triangleSet.indices.setDataUint(vertexSet.indices); - } else { - QTriangulator triangulator; - triangulator.initialize(path, matrix, lod); - QVertexSet vertexSet = triangulator.triangulate(); - triangleSet.vertices = vertexSet.vertices; - triangleSet.indices.setDataUshort(vertexSet.indices); - } - return triangleSet; -} - -QPolylineSet qPolyline(const QVectorPath &path, - const QTransform &matrix, qreal lod) -{ - QPolylineSet polyLineSet; - if (hasElementIndexUint()) { - QTriangulator triangulator; - triangulator.initialize(path, matrix, lod); - QVertexSet vertexSet = triangulator.polyline(); - polyLineSet.vertices = vertexSet.vertices; - polyLineSet.indices.setDataUint(vertexSet.indices); - } else { - QTriangulator triangulator; - triangulator.initialize(path, matrix, lod); - QVertexSet vertexSet = triangulator.polyline(); - polyLineSet.vertices = vertexSet.vertices; - polyLineSet.indices.setDataUshort(vertexSet.indices); - } - return polyLineSet; -} - -QPolylineSet qPolyline(const QPainterPath &path, - const QTransform &matrix, qreal lod) -{ - QPolylineSet polyLineSet; - if (hasElementIndexUint()) { - QTriangulator triangulator; - triangulator.initialize(path, matrix, lod); - QVertexSet vertexSet = triangulator.polyline(); - polyLineSet.vertices = vertexSet.vertices; - polyLineSet.indices.setDataUint(vertexSet.indices); - } else { - QTriangulator triangulator; - triangulator.initialize(path, matrix, lod); - QVertexSet vertexSet = triangulator.polyline(); - polyLineSet.vertices = vertexSet.vertices; - polyLineSet.indices.setDataUshort(vertexSet.indices); - } - return polyLineSet; -} - -QT_END_NAMESPACE diff --git a/src/gui/opengl/qtriangulator_p.h b/src/gui/opengl/qtriangulator_p.h deleted file mode 100644 index 4d1aba099c..0000000000 --- a/src/gui/opengl/qtriangulator_p.h +++ /dev/null @@ -1,148 +0,0 @@ -/**************************************************************************** -** -** 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. -** -** $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 QTRIANGULATOR_P_H -#define QTRIANGULATOR_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 -#include -#include - -QT_BEGIN_NAMESPACE - -class Q_GUI_EXPORT QVertexIndexVector -{ -public: - enum Type { - UnsignedInt, - UnsignedShort - }; - - inline Type type() const { return t; } - - inline void setDataUint(const QVector &data) - { - t = UnsignedInt; - indices32 = data; - } - - inline void setDataUshort(const QVector &data) - { - t = UnsignedShort; - indices16 = data; - } - - inline const void* data() const - { - if (t == UnsignedInt) - return indices32.data(); - return indices16.data(); - } - - inline int size() const - { - if (t == UnsignedInt) - return indices32.size(); - return indices16.size(); - } - - inline QVertexIndexVector &operator = (const QVertexIndexVector &other) - { - if (t == UnsignedInt) - indices32 = other.indices32; - else - indices16 = other.indices16; - - t = other.t; - return *this; - } - -private: - - Type t; - QVector indices32; - QVector indices16; -}; - -struct Q_GUI_EXPORT QTriangleSet -{ - inline QTriangleSet() { } - inline QTriangleSet(const QTriangleSet &other) : vertices(other.vertices), indices(other.indices) { } - QTriangleSet &operator = (const QTriangleSet &other) {vertices = other.vertices; indices = other.indices; return *this;} - - // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ... - QVector vertices; // [x[0], y[0], x[1], y[1], x[2], ...] - QVertexIndexVector indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...] -}; - -struct Q_GUI_EXPORT QPolylineSet -{ - inline QPolylineSet() { } - inline QPolylineSet(const QPolylineSet &other) : vertices(other.vertices), indices(other.indices) { } - QPolylineSet &operator = (const QPolylineSet &other) {vertices = other.vertices; indices = other.indices; return *this;} - - QVector vertices; // [x[0], y[0], x[1], y[1], x[2], ...] - QVertexIndexVector indices; // End of polyline is marked with -1. -}; - -// The vertex coordinates of the returned triangle set will be rounded to a grid with a mesh size -// of 1/32. The polygon is first transformed, then scaled by 32, the coordinates are rounded to -// integers, the polygon is triangulated, and then scaled back by 1/32. -// 'hint' should be a combination of QVectorPath::Hints. -// 'lod' is the level of detail. Default is 1. Curves are split into more lines when 'lod' is higher. -QTriangleSet Q_GUI_EXPORT qTriangulate(const qreal *polygon, int count, uint hint = QVectorPath::PolygonHint | QVectorPath::OddEvenFill, const QTransform &matrix = QTransform()); -QTriangleSet Q_GUI_EXPORT qTriangulate(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); -QTriangleSet Q_GUI_EXPORT qTriangulate(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); -QPolylineSet qPolyline(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); -QPolylineSet Q_GUI_EXPORT qPolyline(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); - -QT_END_NAMESPACE - -#endif diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 2f927aeddb..00f2375923 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -42,6 +42,7 @@ HEADERS += \ painting/qpolygonclipper_p.h \ painting/qrasterdefs_p.h \ painting/qrasterizer_p.h \ + painting/qrbtree_p.h \ painting/qregion.h \ painting/qrgb.h \ painting/qrgba64.h \ @@ -49,6 +50,8 @@ HEADERS += \ painting/qstroker_p.h \ painting/qtextureglyphcache_p.h \ painting/qtransform.h \ + painting/qtriangulatingstroker_p.h \ + painting/qtriangulator_p.h \ painting/qplatformbackingstore.h \ painting/qpathsimplifier_p.h @@ -92,6 +95,8 @@ SOURCES += \ painting/qstroker.cpp \ painting/qtextureglyphcache.cpp \ painting/qtransform.cpp \ + painting/qtriangulatingstroker.cpp \ + painting/qtriangulator.cpp \ painting/qplatformbackingstore.cpp \ painting/qpathsimplifier.cpp diff --git a/src/gui/painting/qrbtree_p.h b/src/gui/painting/qrbtree_p.h new file mode 100644 index 0000000000..d3ee23a91c --- /dev/null +++ b/src/gui/painting/qrbtree_p.h @@ -0,0 +1,571 @@ +/**************************************************************************** +** +** 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. +** +** $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 QRBTREE_P_H +#define QRBTREE_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 + +QT_BEGIN_NAMESPACE + +template +struct QRBTree +{ + struct Node + { + inline Node() : parent(0), left(0), right(0), red(true) { } + inline ~Node() {if (left) delete left; if (right) delete right;} + T data; + Node *parent; + Node *left; + Node *right; + bool red; + }; + + inline QRBTree() : root(0), freeList(0) { } + inline ~QRBTree(); + + inline void clear(); + + void attachBefore(Node *parent, Node *child); + void attachAfter(Node *parent, Node *child); + + inline Node *front(Node *node) const; + inline Node *back(Node *node) const; + Node *next(Node *node) const; + Node *previous(Node *node) const; + + inline void deleteNode(Node *&node); + inline Node *newNode(); + + // Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise. + // 'left' and 'right' cannot be null. + int order(Node *left, Node *right); + inline bool validate() const; + +private: + void rotateLeft(Node *node); + void rotateRight(Node *node); + void update(Node *node); + + inline void attachLeft(Node *parent, Node *child); + inline void attachRight(Node *parent, Node *child); + + int blackDepth(Node *top) const; + bool checkRedBlackProperty(Node *top) const; + + void swapNodes(Node *n1, Node *n2); + void detach(Node *node); + + // 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. + void rebalance(Node *node); + +public: + Node *root; +private: + Node *freeList; +}; + +template +inline QRBTree::~QRBTree() +{ + clear(); + while (freeList) { + // Avoid recursively calling the destructor, as this list may become large. + Node *next = freeList->right; + freeList->right = 0; + delete freeList; + freeList = next; + } +} + +template +inline void QRBTree::clear() +{ + if (root) + delete root; + root = 0; +} + +template +void QRBTree::rotateLeft(Node *node) +{ + // | | // + // N B // + // / \ / \ // + // A B ---> N D // + // / \ / \ // + // C D A C // + + Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); + ref = node->right; + node->right->parent = node->parent; + + // : // + // N // + // / :| // + // A B // + // / \ // + // C D // + + node->right = ref->left; + if (ref->left) + ref->left->parent = node; + + // : | // + // N B // + // / \ : \ // + // A C D // + + ref->left = node; + node->parent = ref; + + // | // + // B // + // / \ // + // N D // + // / \ // + // A C // +} + +template +void QRBTree::rotateRight(Node *node) +{ + // | | // + // N A // + // / \ / \ // + // A B ---> C N // + // / \ / \ // + // C D D B // + + Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); + ref = node->left; + node->left->parent = node->parent; + + node->left = ref->right; + if (ref->right) + ref->right->parent = node; + + ref->right = node; + node->parent = ref; +} + +template +void QRBTree::update(Node *node) // call this after inserting a node +{ + for (;;) { + Node *parent = node->parent; + + // if the node is the root, color it black + if (!parent) { + node->red = false; + return; + } + + // if the parent is black, the node can be left red + if (!parent->red) + return; + + // at this point, the parent is red and cannot be the root + Node *grandpa = parent->parent; + Q_ASSERT(grandpa); + + Node *uncle = (parent == grandpa->left ? grandpa->right : grandpa->left); + if (uncle && uncle->red) { + // grandpa's black, parent and uncle are red. + // let parent and uncle be black, grandpa red and recursively update grandpa. + Q_ASSERT(!grandpa->red); + parent->red = false; + uncle->red = false; + grandpa->red = true; + node = grandpa; + continue; + } + + // at this point, uncle is black + if (node == parent->right && parent == grandpa->left) + rotateLeft(node = parent); + else if (node == parent->left && parent == grandpa->right) + rotateRight(node = parent); + parent = node->parent; + + if (parent == grandpa->left) { + rotateRight(grandpa); + parent->red = false; + grandpa->red = true; + } else { + rotateLeft(grandpa); + parent->red = false; + grandpa->red = true; + } + return; + } +} + +template +inline void QRBTree::attachLeft(Node *parent, Node *child) +{ + Q_ASSERT(!parent->left); + parent->left = child; + child->parent = parent; + update(child); +} + +template +inline void QRBTree::attachRight(Node *parent, Node *child) +{ + Q_ASSERT(!parent->right); + parent->right = child; + child->parent = parent; + update(child); +} + +template +void QRBTree::attachBefore(Node *parent, Node *child) +{ + if (!root) + update(root = child); + else if (!parent) + attachRight(back(root), child); + else if (parent->left) + attachRight(back(parent->left), child); + else + attachLeft(parent, child); +} + +template +void QRBTree::attachAfter(Node *parent, Node *child) +{ + if (!root) + update(root = child); + else if (!parent) + attachLeft(front(root), child); + else if (parent->right) + attachLeft(front(parent->right), child); + else + attachRight(parent, child); +} + +template +void QRBTree::swapNodes(Node *n1, Node *n2) +{ + // Since iterators must not be invalidated, it is not sufficient to only swap the data. + if (n1->parent == n2) { + n1->parent = n2->parent; + n2->parent = n1; + } else if (n2->parent == n1) { + n2->parent = n1->parent; + n1->parent = n2; + } else { + qSwap(n1->parent, n2->parent); + } + + qSwap(n1->left, n2->left); + qSwap(n1->right, n2->right); + qSwap(n1->red, n2->red); + + if (n1->parent) { + if (n1->parent->left == n2) + n1->parent->left = n1; + else + n1->parent->right = n1; + } else { + root = n1; + } + + if (n2->parent) { + if (n2->parent->left == n1) + n2->parent->left = n2; + else + n2->parent->right = n2; + } else { + root = n2; + } + + if (n1->left) + n1->left->parent = n1; + if (n1->right) + n1->right->parent = n1; + + if (n2->left) + n2->left->parent = n2; + if (n2->right) + n2->right->parent = n2; +} + +template +void QRBTree::detach(Node *node) // call this before removing a node. +{ + if (node->right) + swapNodes(node, front(node->right)); + + Node *child = (node->left ? node->left : node->right); + + if (!node->red) { + if (child && child->red) + child->red = false; + else + rebalance(node); + } + + Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); + ref = child; + if (child) + child->parent = node->parent; + node->left = node->right = node->parent = 0; +} + +// 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. +template +void QRBTree::rebalance(Node *node) +{ + Q_ASSERT(!node->red); + for (;;) { + if (!node->parent) + return; + + // at this point, node is not a parent, it is black, thus it must have a sibling. + Node *sibling = (node == node->parent->left ? node->parent->right : node->parent->left); + Q_ASSERT(sibling); + + if (sibling->red) { + sibling->red = false; + node->parent->red = true; + if (node == node->parent->left) + rotateLeft(node->parent); + else + rotateRight(node->parent); + sibling = (node == node->parent->left ? node->parent->right : node->parent->left); + Q_ASSERT(sibling); + } + + // at this point, the sibling is black. + Q_ASSERT(!sibling->red); + + if ((!sibling->left || !sibling->left->red) && (!sibling->right || !sibling->right->red)) { + bool parentWasRed = node->parent->red; + sibling->red = true; + node->parent->red = false; + if (parentWasRed) + return; + node = node->parent; + continue; + } + + // at this point, at least one of the sibling's children is red. + + if (node == node->parent->left) { + if (!sibling->right || !sibling->right->red) { + Q_ASSERT(sibling->left); + sibling->red = true; + sibling->left->red = false; + rotateRight(sibling); + + sibling = sibling->parent; + Q_ASSERT(sibling); + } + sibling->red = node->parent->red; + node->parent->red = false; + + Q_ASSERT(sibling->right->red); + sibling->right->red = false; + rotateLeft(node->parent); + } else { + if (!sibling->left || !sibling->left->red) { + Q_ASSERT(sibling->right); + sibling->red = true; + sibling->right->red = false; + rotateLeft(sibling); + + sibling = sibling->parent; + Q_ASSERT(sibling); + } + sibling->red = node->parent->red; + node->parent->red = false; + + Q_ASSERT(sibling->left->red); + sibling->left->red = false; + rotateRight(node->parent); + } + return; + } +} + +template +inline typename QRBTree::Node *QRBTree::front(Node *node) const +{ + while (node->left) + node = node->left; + return node; +} + +template +inline typename QRBTree::Node *QRBTree::back(Node *node) const +{ + while (node->right) + node = node->right; + return node; +} + +template +typename QRBTree::Node *QRBTree::next(Node *node) const +{ + if (node->right) + return front(node->right); + while (node->parent && node == node->parent->right) + node = node->parent; + return node->parent; +} + +template +typename QRBTree::Node *QRBTree::previous(Node *node) const +{ + if (node->left) + return back(node->left); + while (node->parent && node == node->parent->left) + node = node->parent; + return node->parent; +} + +template +int QRBTree::blackDepth(Node *top) const +{ + if (!top) + return 0; + int leftDepth = blackDepth(top->left); + int rightDepth = blackDepth(top->right); + if (leftDepth != rightDepth) + return -1; + if (!top->red) + ++leftDepth; + return leftDepth; +} + +template +bool QRBTree::checkRedBlackProperty(Node *top) const +{ + if (!top) + return true; + if (top->left && !checkRedBlackProperty(top->left)) + return false; + if (top->right && !checkRedBlackProperty(top->right)) + return false; + return !(top->red && ((top->left && top->left->red) || (top->right && top->right->red))); +} + +template +inline bool QRBTree::validate() const +{ + return checkRedBlackProperty(root) && blackDepth(root) != -1; +} + +template +inline void QRBTree::deleteNode(Node *&node) +{ + Q_ASSERT(node); + detach(node); + node->right = freeList; + freeList = node; + node = 0; +} + +template +inline typename QRBTree::Node *QRBTree::newNode() +{ + if (freeList) { + Node *node = freeList; + freeList = freeList->right; + node->parent = node->left = node->right = 0; + node->red = true; + return node; + } + return new Node; +} + +// Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise. +// 'left' and 'right' cannot be null. +template +int QRBTree::order(Node *left, Node *right) +{ + Q_ASSERT(left && right); + if (left == right) + return 0; + + QVector leftAncestors; + QVector rightAncestors; + while (left) { + leftAncestors.push_back(left); + left = left->parent; + } + while (right) { + rightAncestors.push_back(right); + right = right->parent; + } + Q_ASSERT(leftAncestors.back() == root && rightAncestors.back() == root); + + while (!leftAncestors.empty() && !rightAncestors.empty() && leftAncestors.back() == rightAncestors.back()) { + leftAncestors.pop_back(); + rightAncestors.pop_back(); + } + + if (!leftAncestors.empty()) + return (leftAncestors.back() == leftAncestors.back()->parent->left ? -1 : 1); + + if (!rightAncestors.empty()) + return (rightAncestors.back() == rightAncestors.back()->parent->right ? -1 : 1); + + // The code should never reach this point. + Q_ASSERT(!leftAncestors.empty() || !rightAncestors.empty()); + return 0; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/painting/qtriangulatingstroker.cpp b/src/gui/painting/qtriangulatingstroker.cpp new file mode 100644 index 0000000000..d9a3231165 --- /dev/null +++ b/src/gui/painting/qtriangulatingstroker.cpp @@ -0,0 +1,613 @@ +/**************************************************************************** +** +** 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. +** +** $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 "qtriangulatingstroker_p.h" +#include + +QT_BEGIN_NAMESPACE + +#define CURVE_FLATNESS Q_PI / 8 + + + + +void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur, + bool implicitClose, bool endsAtStart) +{ + if (endsAtStart) { + join(start + 2); + } else if (implicitClose) { + join(start); + lineTo(start); + join(start+2); + } else { + endCap(cur); + } + int count = m_vertices.size(); + + // Copy the (x, y) values because QDataBuffer::add(const float& t) + // may resize the buffer, which will leave t pointing at the + // previous buffer's memory region if we don't copy first. + float x = m_vertices.at(count-2); + float y = m_vertices.at(count-1); + m_vertices.add(x); + m_vertices.add(y); +} + +static inline void skipDuplicatePoints(const qreal **pts, const qreal *endPts) +{ + while ((*pts + 2) < endPts && float((*pts)[0]) == float((*pts)[2]) + && float((*pts)[1]) == float((*pts)[3])) + { + *pts += 2; + } +} + +void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, const QRectF &, QPainter::RenderHints hints) +{ + const qreal *pts = path.points(); + const QPainterPath::ElementType *types = path.elements(); + int count = path.elementCount(); + if (count < 2) + return; + + float realWidth = qpen_widthf(pen); + if (realWidth == 0) + realWidth = 1; + + m_width = realWidth / 2; + + bool cosmetic = qt_pen_is_cosmetic(pen, hints); + if (cosmetic) { + m_width = m_width * m_inv_scale; + } + + m_join_style = qpen_joinStyle(pen); + m_cap_style = qpen_capStyle(pen); + m_vertices.reset(); + m_miter_limit = pen.miterLimit() * qpen_widthf(pen); + + // The curvyness is based on the notion that I originally wanted + // roughly one line segment pr 4 pixels. This may seem little, but + // because we sample at constantly incrementing B(t) E [0(4, realWidth * CURVE_FLATNESS); + } else { + m_curvyness_add = m_width; + m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; + m_roundness = qMax(4, realWidth * m_curvyness_mul); + } + + // Over this level of segmentation, there doesn't seem to be any + // benefit, even for huge penWidth + if (m_roundness > 24) + m_roundness = 24; + + m_sin_theta = qFastSin(Q_PI / m_roundness); + m_cos_theta = qFastCos(Q_PI / m_roundness); + + const qreal *endPts = pts + (count<<1); + const qreal *startPts = 0; + + Qt::PenCapStyle cap = m_cap_style; + + if (!types) { + skipDuplicatePoints(&pts, endPts); + if ((pts + 2) == endPts) + return; + + startPts = pts; + + bool endsAtStart = float(startPts[0]) == float(endPts[-2]) + && float(startPts[1]) == float(endPts[-1]); + + if (endsAtStart || path.hasImplicitClose()) + m_cap_style = Qt::FlatCap; + moveTo(pts); + m_cap_style = cap; + pts += 2; + skipDuplicatePoints(&pts, endPts); + lineTo(pts); + pts += 2; + skipDuplicatePoints(&pts, endPts); + while (pts < endPts) { + join(pts); + lineTo(pts); + pts += 2; + skipDuplicatePoints(&pts, endPts); + } + endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); + + } else { + bool endsAtStart = false; + QPainterPath::ElementType previousType = QPainterPath::MoveToElement; + const qreal *previousPts = pts; + while (pts < endPts) { + switch (*types) { + case QPainterPath::MoveToElement: { + if (previousType != QPainterPath::MoveToElement) + endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart); + + startPts = pts; + skipDuplicatePoints(&startPts, endPts); // Skip duplicates to find correct normal. + if (startPts + 2 >= endPts) + return; // Nothing to see here... + + int end = (endPts - pts) / 2; + int i = 2; // Start looking to ahead since we never have two moveto's in a row + while (i points; + arcPoints(m_cx, m_cy, m_cx + m_nvx, m_cy + m_nvy, m_cx - m_nvx, m_cy - m_nvy, points); + m_vertices.resize(m_vertices.size() + points.size() + 2 * int(invisibleJump)); + int count = m_vertices.size(); + int front = 0; + int end = points.size() / 2; + while (front != end) { + m_vertices.at(--count) = points[2 * end - 1]; + m_vertices.at(--count) = points[2 * end - 2]; + --end; + if (front == end) + break; + m_vertices.at(--count) = points[2 * front + 1]; + m_vertices.at(--count) = points[2 * front + 0]; + ++front; + } + + if (invisibleJump) { + m_vertices.at(count - 1) = m_vertices.at(count + 1); + m_vertices.at(count - 2) = m_vertices.at(count + 0); + } + break; } + default: break; // ssssh gcc... + } + emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); +} + +void QTriangulatingStroker::cubicTo(const qreal *pts) +{ + const QPointF *p = (const QPointF *) pts; + QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]); + + QRectF bounds = bezier.bounds(); + float rad = qMax(bounds.width(), bounds.height()); + int threshold = qMin(64, (rad + m_curvyness_add) * m_curvyness_mul); + if (threshold < 4) + threshold = 4; + qreal threshold_minus_1 = threshold - 1; + float vx, vy; + + float cx = m_cx, cy = m_cy; + float x, y; + + for (int i=1; i (pts[0], pts[1]) + normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy); + + switch (m_join_style) { + case Qt::BevelJoin: + break; + case Qt::SvgMiterJoin: + case Qt::MiterJoin: { + // Find out on which side the join should be. + int count = m_vertices.size(); + float prevNvx = m_vertices.at(count - 2) - m_cx; + float prevNvy = m_vertices.at(count - 1) - m_cy; + float xprod = prevNvx * m_nvy - prevNvy * m_nvx; + float px, py, qx, qy; + + // If the segments are parallel, use bevel join. + if (qFuzzyIsNull(xprod)) + break; + + // Find the corners of the previous and next segment to join. + if (xprod < 0) { + px = m_vertices.at(count - 2); + py = m_vertices.at(count - 1); + qx = m_cx - m_nvx; + qy = m_cy - m_nvy; + } else { + px = m_vertices.at(count - 4); + py = m_vertices.at(count - 3); + qx = m_cx + m_nvx; + qy = m_cy + m_nvy; + } + + // Find intersection point. + float pu = px * prevNvx + py * prevNvy; + float qv = qx * m_nvx + qy * m_nvy; + float ix = (m_nvy * pu - prevNvy * qv) / xprod; + float iy = (prevNvx * qv - m_nvx * pu) / xprod; + + // Check that the distance to the intersection point is less than the miter limit. + if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= m_miter_limit * m_miter_limit) { + m_vertices.add(ix); + m_vertices.add(iy); + m_vertices.add(ix); + m_vertices.add(iy); + } + // else + // Do a plain bevel join if the miter limit is exceeded or if + // the lines are parallel. This is not what the raster + // engine's stroker does, but it is both faster and similar to + // what some other graphics API's do. + + break; } + case Qt::RoundJoin: { + QVarLengthArray points; + int count = m_vertices.size(); + float prevNvx = m_vertices.at(count - 2) - m_cx; + float prevNvy = m_vertices.at(count - 1) - m_cy; + if (m_nvx * prevNvy - m_nvy * prevNvx < 0) { + arcPoints(0, 0, m_nvx, m_nvy, -prevNvx, -prevNvy, points); + for (int i = points.size() / 2; i > 0; --i) + emitLineSegment(m_cx, m_cy, points[2 * i - 2], points[2 * i - 1]); + } else { + arcPoints(0, 0, -prevNvx, -prevNvy, m_nvx, m_nvy, points); + for (int i = 0; i < points.size() / 2; ++i) + emitLineSegment(m_cx, m_cy, points[2 * i + 0], points[2 * i + 1]); + } + break; } + default: break; // gcc warn-- + } + + emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); +} + +void QTriangulatingStroker::endCap(const qreal *) +{ + switch (m_cap_style) { + case Qt::FlatCap: + break; + case Qt::SquareCap: + emitLineSegment(m_cx + m_nvy, m_cy - m_nvx, m_nvx, m_nvy); + break; + case Qt::RoundCap: { + QVarLengthArray points; + int count = m_vertices.size(); + arcPoints(m_cx, m_cy, m_vertices.at(count - 2), m_vertices.at(count - 1), m_vertices.at(count - 4), m_vertices.at(count - 3), points); + int front = 0; + int end = points.size() / 2; + while (front != end) { + m_vertices.add(points[2 * end - 2]); + m_vertices.add(points[2 * end - 1]); + --end; + if (front == end) + break; + m_vertices.add(points[2 * front + 0]); + m_vertices.add(points[2 * front + 1]); + ++front; + } + break; } + default: break; // to shut gcc up... + } +} + +void QTriangulatingStroker::arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray &points) +{ + float dx1 = fromX - cx; + float dy1 = fromY - cy; + float dx2 = toX - cx; + float dy2 = toY - cy; + + // while more than 180 degrees left: + while (dx1 * dy2 - dx2 * dy1 < 0) { + float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; + float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; + dx1 = tmpx; + dy1 = tmpy; + points.append(cx + dx1); + points.append(cy + dy1); + } + + // while more than 90 degrees left: + while (dx1 * dx2 + dy1 * dy2 < 0) { + float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; + float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; + dx1 = tmpx; + dy1 = tmpy; + points.append(cx + dx1); + points.append(cy + dy1); + } + + // while more than 0 degrees left: + while (dx1 * dy2 - dx2 * dy1 > 0) { + float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; + float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; + dx1 = tmpx; + dy1 = tmpy; + points.append(cx + dx1); + points.append(cy + dy1); + } + + // remove last point which was rotated beyond [toX, toY]. + if (!points.isEmpty()) + points.resize(points.size() - 2); +} + +static void qdashprocessor_moveTo(qreal x, qreal y, void *data) +{ + ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y); +} + +static void qdashprocessor_lineTo(qreal x, qreal y, void *data) +{ + ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y); +} + +static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *) +{ + Q_ASSERT(0); // The dasher should not produce curves... +} + +QDashedStrokeProcessor::QDashedStrokeProcessor() + : m_points(0), m_types(0), + m_dash_stroker(0), m_inv_scale(1) +{ + m_dash_stroker.setMoveToHook(qdashprocessor_moveTo); + m_dash_stroker.setLineToHook(qdashprocessor_lineTo); + m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo); +} + +void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints) +{ + + const qreal *pts = path.points(); + const QPainterPath::ElementType *types = path.elements(); + int count = path.elementCount(); + + bool cosmetic = qt_pen_is_cosmetic(pen, hints); + + m_points.reset(); + m_types.reset(); + m_points.reserve(path.elementCount()); + m_types.reserve(path.elementCount()); + + qreal width = qpen_widthf(pen); + if (width == 0) + width = 1; + + m_dash_stroker.setDashPattern(pen.dashPattern()); + m_dash_stroker.setStrokeWidth(cosmetic ? width * m_inv_scale : width); + m_dash_stroker.setDashOffset(pen.dashOffset()); + m_dash_stroker.setMiterLimit(pen.miterLimit()); + m_dash_stroker.setClipRect(clip); + + float curvynessAdd, curvynessMul; + + // simplify pens that are thin in device size (2px wide or less) + if (width < 2.5 && (cosmetic || m_inv_scale == 1)) { + curvynessAdd = 0.5; + curvynessMul = CURVE_FLATNESS / m_inv_scale; + } else if (cosmetic) { + curvynessAdd= width / 2; + curvynessMul= float(CURVE_FLATNESS); + } else { + curvynessAdd = width * m_inv_scale; + curvynessMul = CURVE_FLATNESS / m_inv_scale; + } + + if (count < 2) + return; + + const qreal *endPts = pts + (count<<1); + + m_dash_stroker.begin(this); + + if (!types) { + m_dash_stroker.moveTo(pts[0], pts[1]); + pts += 2; + while (pts < endPts) { + m_dash_stroker.lineTo(pts[0], pts[1]); + pts += 2; + } + } else { + while (pts < endPts) { + switch (*types) { + case QPainterPath::MoveToElement: + m_dash_stroker.moveTo(pts[0], pts[1]); + pts += 2; + ++types; + break; + case QPainterPath::LineToElement: + m_dash_stroker.lineTo(pts[0], pts[1]); + pts += 2; + ++types; + break; + case QPainterPath::CurveToElement: { + QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1), + *(((const QPointF *) pts)), + *(((const QPointF *) pts) + 1), + *(((const QPointF *) pts) + 2)); + QRectF bounds = b.bounds(); + float rad = qMax(bounds.width(), bounds.height()); + int threshold = qMin(64, (rad + curvynessAdd) * curvynessMul); + if (threshold < 4) + threshold = 4; + + qreal threshold_minus_1 = threshold - 1; + for (int i=0; i +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QTriangulatingStroker +{ +public: + QTriangulatingStroker() : m_vertices(0), m_cx(0), m_cy(0), m_nvx(0), m_nvy(0), m_width(1), m_miter_limit(2), + m_roundness(0), m_sin_theta(0), m_cos_theta(0), m_inv_scale(1), m_curvyness_mul(1), m_curvyness_add(0), + m_join_style(Qt::BevelJoin), m_cap_style(Qt::SquareCap) {} + + void process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints); + + inline int vertexCount() const { return m_vertices.size(); } + inline const float *vertices() const { return m_vertices.data(); } + + inline void setInvScale(qreal invScale) { m_inv_scale = invScale; } + +private: + inline void emitLineSegment(float x, float y, float nx, float ny); + void moveTo(const qreal *pts); + inline void lineTo(const qreal *pts); + void cubicTo(const qreal *pts); + void join(const qreal *pts); + inline void normalVector(float x1, float y1, float x2, float y2, float *nx, float *ny); + void endCap(const qreal *pts); + void arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray &points); + void endCapOrJoinClosed(const qreal *start, const qreal *cur, bool implicitClose, bool endsAtStart); + + + QDataBuffer m_vertices; + + float m_cx, m_cy; // current points + float m_nvx, m_nvy; // normal vector... + float m_width; + qreal m_miter_limit; + + int m_roundness; // Number of line segments in a round join + qreal m_sin_theta; // sin(m_roundness / 360); + qreal m_cos_theta; // cos(m_roundness / 360); + qreal m_inv_scale; + float m_curvyness_mul; + float m_curvyness_add; + + Qt::PenJoinStyle m_join_style; + Qt::PenCapStyle m_cap_style; +}; + +class Q_GUI_EXPORT QDashedStrokeProcessor +{ +public: + QDashedStrokeProcessor(); + + void process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints); + + inline void addElement(QPainterPath::ElementType type, qreal x, qreal y) { + m_points.add(x); + m_points.add(y); + m_types.add(type); + } + + inline int elementCount() const { return m_types.size(); } + inline qreal *points() const { return m_points.data(); } + inline QPainterPath::ElementType *elementTypes() const { return m_types.data(); } + + inline void setInvScale(qreal invScale) { m_inv_scale = invScale; } + +private: + QDataBuffer m_points; + QDataBuffer m_types; + QDashStroker m_dash_stroker; + qreal m_inv_scale; +}; + +inline void QTriangulatingStroker::normalVector(float x1, float y1, float x2, float y2, + float *nx, float *ny) +{ + float dx = x2 - x1; + float dy = y2 - y1; + Q_ASSERT(dx != 0 || dy != 0); + + float pw; + + if (dx == 0) + pw = m_width / std::abs(dy); + else if (dy == 0) + pw = m_width / std::abs(dx); + else + pw = m_width / std::sqrt(dx*dx + dy*dy); + + *nx = -dy * pw; + *ny = dx * pw; +} + +inline void QTriangulatingStroker::emitLineSegment(float x, float y, float vx, float vy) +{ + m_vertices.add(x + vx); + m_vertices.add(y + vy); + m_vertices.add(x - vx); + m_vertices.add(y - vy); +} + +void QTriangulatingStroker::lineTo(const qreal *pts) +{ + emitLineSegment(pts[0], pts[1], m_nvx, m_nvy); + m_cx = pts[0]; + m_cy = pts[1]; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/painting/qtriangulator.cpp b/src/gui/painting/qtriangulator.cpp new file mode 100644 index 0000000000..7906011cd2 --- /dev/null +++ b/src/gui/painting/qtriangulator.cpp @@ -0,0 +1,2382 @@ +/**************************************************************************** +** +** 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. +** +** $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 "qtriangulator_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QT_NO_OPENGL +# include +# include +#endif +#include + +QT_BEGIN_NAMESPACE + +//#define Q_TRIANGULATOR_DEBUG + +#define Q_FIXED_POINT_SCALE 32 + +template +struct QVertexSet +{ + inline QVertexSet() { } + inline QVertexSet(const QVertexSet &other) : vertices(other.vertices), indices(other.indices) { } + QVertexSet &operator = (const QVertexSet &other) {vertices = other.vertices; indices = other.indices; return *this;} + + // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ... + QVector vertices; // [x[0], y[0], x[1], y[1], x[2], ...] + QVector indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...] +}; + +//============================================================================// +// QFraction // +//============================================================================// + +// Fraction must be in the range [0, 1) +struct QFraction +{ + // Comparison operators must not be called on invalid fractions. + inline bool operator < (const QFraction &other) const; + inline bool operator == (const QFraction &other) const; + inline bool operator != (const QFraction &other) const {return !(*this == other);} + inline bool operator > (const QFraction &other) const {return other < *this;} + inline bool operator >= (const QFraction &other) const {return !(*this < other);} + inline bool operator <= (const QFraction &other) const {return !(*this > other);} + + inline bool isValid() const {return denominator != 0;} + + // numerator and denominator must not have common denominators. + quint64 numerator, denominator; +}; + +static inline quint64 gcd(quint64 x, quint64 y) +{ + while (y != 0) { + quint64 z = y; + y = x % y; + x = z; + } + return x; +} + +static inline int compare(quint64 a, quint64 b) +{ + return (a > b) - (a < b); +} + +// Compare a/b with c/d. +// Return negative if less, 0 if equal, positive if greater. +// a < b, c < d +static int qCompareFractions(quint64 a, quint64 b, quint64 c, quint64 d) +{ + const quint64 LIMIT = Q_UINT64_C(0x100000000); + for (;;) { + // If the products 'ad' and 'bc' fit into 64 bits, they can be directly compared. + if (b < LIMIT && d < LIMIT) + return compare(a * d, b * c); + + if (a == 0 || c == 0) + return compare(a, c); + + // a/b < c/d <=> d/c < b/a + quint64 b_div_a = b / a; + quint64 d_div_c = d / c; + if (b_div_a != d_div_c) + return compare(d_div_c, b_div_a); + + // floor(d/c) == floor(b/a) + // frac(d/c) < frac(b/a) ? + // frac(x/y) = (x%y)/y + d -= d_div_c * c; //d %= c; + b -= b_div_a * a; //b %= a; + qSwap(a, d); + qSwap(b, c); + } +} + +// Fraction must be in the range [0, 1) +// Assume input is valid. +static QFraction qFraction(quint64 n, quint64 d) { + QFraction result; + if (n == 0) { + result.numerator = 0; + result.denominator = 1; + } else { + quint64 g = gcd(n, d); + result.numerator = n / g; + result.denominator = d / g; + } + return result; +} + +inline bool QFraction::operator < (const QFraction &other) const +{ + return qCompareFractions(numerator, denominator, other.numerator, other.denominator) < 0; +} + +inline bool QFraction::operator == (const QFraction &other) const +{ + return numerator == other.numerator && denominator == other.denominator; +} + +//============================================================================// +// QPodPoint // +//============================================================================// + +struct QPodPoint +{ + inline bool operator < (const QPodPoint &other) const + { + if (y != other.y) + return y < other.y; + return x < other.x; + } + + inline bool operator > (const QPodPoint &other) const {return other < *this;} + inline bool operator <= (const QPodPoint &other) const {return !(*this > other);} + inline bool operator >= (const QPodPoint &other) const {return !(*this < other);} + inline bool operator == (const QPodPoint &other) const {return x == other.x && y == other.y;} + inline bool operator != (const QPodPoint &other) const {return x != other.x || y != other.y;} + + inline QPodPoint &operator += (const QPodPoint &other) {x += other.x; y += other.y; return *this;} + inline QPodPoint &operator -= (const QPodPoint &other) {x -= other.x; y -= other.y; return *this;} + inline QPodPoint operator + (const QPodPoint &other) const {QPodPoint result = {x + other.x, y + other.y}; return result;} + inline QPodPoint operator - (const QPodPoint &other) const {QPodPoint result = {x - other.x, y - other.y}; return result;} + + int x; + int y; +}; + +static inline qint64 qCross(const QPodPoint &u, const QPodPoint &v) +{ + return qint64(u.x) * qint64(v.y) - qint64(u.y) * qint64(v.x); +} + +#ifdef Q_TRIANGULATOR_DEBUG +static inline qint64 qDot(const QPodPoint &u, const QPodPoint &v) +{ + return qint64(u.x) * qint64(v.x) + qint64(u.y) * qint64(v.y); +} +#endif + +// Return positive value if 'p' is to the right of the line 'v1'->'v2', negative if left of the +// line and zero if exactly on the line. +// The returned value is the z-component of the qCross product between 'v2-v1' and 'p-v1', +// which is twice the signed area of the triangle 'p'->'v1'->'v2' (positive for CW order). +static inline qint64 qPointDistanceFromLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2) +{ + return qCross(v2 - v1, p - v1); +} + +static inline bool qPointIsLeftOfLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2) +{ + return QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(p, v1, v2) < 0; +} + +//============================================================================// +// QIntersectionPoint // +//============================================================================// + +struct QIntersectionPoint +{ + inline bool isValid() const {return xOffset.isValid() && yOffset.isValid();} + QPodPoint round() const; + inline bool isAccurate() const {return xOffset.numerator == 0 && yOffset.numerator == 0;} + bool operator < (const QIntersectionPoint &other) const; + bool operator == (const QIntersectionPoint &other) const; + inline bool operator != (const QIntersectionPoint &other) const {return !(*this == other);} + inline bool operator > (const QIntersectionPoint &other) const {return other < *this;} + inline bool operator >= (const QIntersectionPoint &other) const {return !(*this < other);} + inline bool operator <= (const QIntersectionPoint &other) const {return !(*this > other);} + bool isOnLine(const QPodPoint &u, const QPodPoint &v) const; + + QPodPoint upperLeft; + QFraction xOffset; + QFraction yOffset; +}; + +static inline QIntersectionPoint qIntersectionPoint(const QPodPoint &point) +{ + // upperLeft = point, xOffset = 0/1, yOffset = 0/1. + QIntersectionPoint p = {{point.x, point.y}, {0, 1}, {0, 1}}; + return p; +} + +static QIntersectionPoint qIntersectionPoint(const QPodPoint &u1, const QPodPoint &u2, const QPodPoint &v1, const QPodPoint &v2) +{ + QIntersectionPoint result = {{0, 0}, {0, 0}, {0, 0}}; + + QPodPoint u = u2 - u1; + QPodPoint v = v2 - v1; + qint64 d1 = qCross(u, v1 - u1); + qint64 d2 = qCross(u, v2 - u1); + qint64 det = d2 - d1; + qint64 d3 = qCross(v, u1 - v1); + qint64 d4 = d3 - det; //qCross(v, u2 - v1); + + // Check that the math is correct. + Q_ASSERT(d4 == qCross(v, u2 - v1)); + + // The intersection point can be expressed as: + // v1 - v * d1/det + // v2 - v * d2/det + // u1 + u * d3/det + // u2 + u * d4/det + + // I'm only interested in lines that are crossing, so ignore parallel lines even if they overlap. + if (det == 0) + return result; + + if (det < 0) { + det = -det; + d1 = -d1; + d2 = -d2; + d3 = -d3; + d4 = -d4; + } + + // I'm only interested in lines intersecting at their interior, not at their end points. + // The lines intersect at their interior if and only if 'd1 < 0', 'd2 > 0', 'd3 < 0' and 'd4 > 0'. + if (d1 >= 0 || d2 <= 0 || d3 <= 0 || d4 >= 0) + return result; + + // Calculate the intersection point as follows: + // v1 - v * d1/det | v1 <= v2 (component-wise) + // v2 - v * d2/det | v2 < v1 (component-wise) + + // Assuming 21 bits per vector component. + // TODO: Make code path for 31 bits per vector component. + if (v.x >= 0) { + result.upperLeft.x = v1.x + (-v.x * d1) / det; + result.xOffset = qFraction(quint64(-v.x * d1) % quint64(det), quint64(det)); + } else { + result.upperLeft.x = v2.x + (-v.x * d2) / det; + result.xOffset = qFraction(quint64(-v.x * d2) % quint64(det), quint64(det)); + } + + if (v.y >= 0) { + result.upperLeft.y = v1.y + (-v.y * d1) / det; + result.yOffset = qFraction(quint64(-v.y * d1) % quint64(det), quint64(det)); + } else { + result.upperLeft.y = v2.y + (-v.y * d2) / det; + result.yOffset = qFraction(quint64(-v.y * d2) % quint64(det), quint64(det)); + } + + Q_ASSERT(result.xOffset.isValid()); + Q_ASSERT(result.yOffset.isValid()); + return result; +} + +QPodPoint QIntersectionPoint::round() const +{ + QPodPoint result = upperLeft; + if (2 * xOffset.numerator >= xOffset.denominator) + ++result.x; + if (2 * yOffset.numerator >= yOffset.denominator) + ++result.y; + return result; +} + +bool QIntersectionPoint::operator < (const QIntersectionPoint &other) const +{ + if (upperLeft.y != other.upperLeft.y) + return upperLeft.y < other.upperLeft.y; + if (yOffset != other.yOffset) + return yOffset < other.yOffset; + if (upperLeft.x != other.upperLeft.x) + return upperLeft.x < other.upperLeft.x; + return xOffset < other.xOffset; +} + +bool QIntersectionPoint::operator == (const QIntersectionPoint &other) const +{ + return upperLeft == other.upperLeft && xOffset == other.xOffset && yOffset == other.yOffset; +} + +// Returns \c true if this point is on the infinite line passing through 'u' and 'v'. +bool QIntersectionPoint::isOnLine(const QPodPoint &u, const QPodPoint &v) const +{ + // TODO: Make code path for coordinates with more than 21 bits. + const QPodPoint p = upperLeft - u; + const QPodPoint q = v - u; + bool isHorizontal = p.y == 0 && yOffset.numerator == 0; + bool isVertical = p.x == 0 && xOffset.numerator == 0; + if (isHorizontal && isVertical) + return true; + if (isHorizontal) + return q.y == 0; + if (q.y == 0) + return false; + if (isVertical) + return q.x == 0; + if (q.x == 0) + return false; + + // At this point, 'p+offset' and 'q' cannot lie on the x or y axis. + + if (((q.x < 0) == (q.y < 0)) != ((p.x < 0) == (p.y < 0))) + return false; // 'p + offset' and 'q' pass through different quadrants. + + // Move all coordinates into the first quadrant. + quint64 nx, ny; + if (p.x < 0) + nx = quint64(-p.x) * xOffset.denominator - xOffset.numerator; + else + nx = quint64(p.x) * xOffset.denominator + xOffset.numerator; + if (p.y < 0) + ny = quint64(-p.y) * yOffset.denominator - yOffset.numerator; + else + ny = quint64(p.y) * yOffset.denominator + yOffset.numerator; + + return qFraction(quint64(qAbs(q.x)) * xOffset.denominator, quint64(qAbs(q.y)) * yOffset.denominator) == qFraction(nx, ny); +} + +//============================================================================// +// QMaxHeap // +//============================================================================// + +template +class QMaxHeap +{ +public: + QMaxHeap() : m_data(0) {} + inline int size() const {return m_data.size();} + inline bool empty() const {return m_data.isEmpty();} + inline bool isEmpty() const {return m_data.isEmpty();} + void push(const T &x); + T pop(); + inline const T &top() const {return m_data.first();} +private: + static inline int parent(int i) {return (i - 1) / 2;} + static inline int left(int i) {return 2 * i + 1;} + static inline int right(int i) {return 2 * i + 2;} + + QDataBuffer m_data; +}; + +template +void QMaxHeap::push(const T &x) +{ + int current = m_data.size(); + int parent = QMaxHeap::parent(current); + m_data.add(x); + while (current != 0 && m_data.at(parent) < x) { + m_data.at(current) = m_data.at(parent); + current = parent; + parent = QMaxHeap::parent(current); + } + m_data.at(current) = x; +} + +template +T QMaxHeap::pop() +{ + T result = m_data.first(); + T back = m_data.last(); + m_data.pop_back(); + if (!m_data.isEmpty()) { + int current = 0; + for (;;) { + int left = QMaxHeap::left(current); + int right = QMaxHeap::right(current); + if (left >= m_data.size()) + break; + int greater = left; + if (right < m_data.size() && m_data.at(left) < m_data.at(right)) + greater = right; + if (m_data.at(greater) < back) + break; + m_data.at(current) = m_data.at(greater); + current = greater; + } + m_data.at(current) = back; + } + return result; +} + +//============================================================================// +// QInt64Hash // +//============================================================================// + +// Copied from qhash.cpp +static const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 17, 27, 3, + 1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0 +}; + +// Copied from qhash.cpp +static inline int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + +static inline int primeForCount(int count) +{ + int low = 0; + int high = 32; + for (int i = 0; i < 5; ++i) { + int mid = (high + low) / 2; + if (uint(count) >= (1u << mid)) + low = mid; + else + high = mid; + } + return primeForNumBits(high); +} + +// Hash set of quint64s. Elements cannot be removed without clearing the +// entire set. A value of -1 is used to mark unused entries. +class QInt64Set +{ +public: + inline QInt64Set(int capacity = 64); + inline ~QInt64Set() {if (m_array) delete[] m_array;} + inline bool isValid() const {return m_array;} + void insert(quint64 key); + bool contains(quint64 key) const; + inline void clear(); +private: + bool rehash(int capacity); + + static const quint64 UNUSED; + + quint64 *m_array; + int m_capacity; + int m_count; +}; + +const quint64 QInt64Set::UNUSED = quint64(-1); + +inline QInt64Set::QInt64Set(int capacity) +{ + m_capacity = primeForCount(capacity); + m_array = new quint64[m_capacity]; + if (m_array) + clear(); + else + m_capacity = 0; +} + +bool QInt64Set::rehash(int capacity) +{ + quint64 *oldArray = m_array; + int oldCapacity = m_capacity; + + m_capacity = capacity; + m_array = new quint64[m_capacity]; + if (m_array) { + clear(); + if (oldArray) { + for (int i = 0; i < oldCapacity; ++i) { + if (oldArray[i] != UNUSED) + insert(oldArray[i]); + } + delete[] oldArray; + } + return true; + } else { + m_capacity = oldCapacity; + m_array = oldArray; + return false; + } +} + +void QInt64Set::insert(quint64 key) +{ + if (m_count > 3 * m_capacity / 4) + rehash(primeForCount(2 * m_capacity)); + Q_ASSERT_X(m_array, "QInt64Hash::insert", "Hash set not allocated."); + int index = int(key % m_capacity); + for (int i = 0; i < m_capacity; ++i) { + index += i; + if (index >= m_capacity) + index -= m_capacity; + if (m_array[index] == key) + return; + if (m_array[index] == UNUSED) { + ++m_count; + m_array[index] = key; + return; + } + } + Q_ASSERT_X(0, "QInt64Hash::insert", "Hash set full."); +} + +bool QInt64Set::contains(quint64 key) const +{ + Q_ASSERT_X(m_array, "QInt64Hash::contains", "Hash set not allocated."); + int index = int(key % m_capacity); + for (int i = 0; i < m_capacity; ++i) { + index += i; + if (index >= m_capacity) + index -= m_capacity; + if (m_array[index] == key) + return true; + if (m_array[index] == UNUSED) + return false; + } + return false; +} + +inline void QInt64Set::clear() +{ + Q_ASSERT_X(m_array, "QInt64Hash::clear", "Hash set not allocated."); + for (int i = 0; i < m_capacity; ++i) + m_array[i] = UNUSED; + m_count = 0; +} + +//============================================================================// +// QTriangulator // +//============================================================================// +template +class QTriangulator +{ +public: + typedef QVarLengthArray ShortArray; + + //================================// + // QTriangulator::ComplexToSimple // + //================================// + friend class ComplexToSimple; + class ComplexToSimple + { + public: + inline ComplexToSimple(QTriangulator *parent) : m_parent(parent), + m_edges(0), m_events(0), m_splits(0) { } + void decompose(); + private: + struct Edge + { + inline int &upper() {return pointingUp ? to : from;} + inline int &lower() {return pointingUp ? from : to;} + inline int upper() const {return pointingUp ? to : from;} + inline int lower() const {return pointingUp ? from : to;} + + QRBTree::Node *node; + int from, to; // vertex + int next, previous; // edge + int winding; + bool mayIntersect; + bool pointingUp, originallyPointingUp; + }; + + struct Intersection + { + bool operator < (const Intersection &other) const {return other.intersectionPoint < intersectionPoint;} + + QIntersectionPoint intersectionPoint; + int vertex; + int leftEdge; + int rightEdge; + }; + + struct Split + { + int vertex; + int edge; + bool accurate; + }; + + struct Event + { + enum Type {Upper, Lower}; + inline bool operator < (const Event &other) const; + + QPodPoint point; + Type type; + int edge; + }; + +#ifdef Q_TRIANGULATOR_DEBUG + friend class DebugDialog; + friend class QTriangulator; + class DebugDialog : public QDialog + { + public: + DebugDialog(ComplexToSimple *parent, int currentVertex); + protected: + void paintEvent(QPaintEvent *); + void wheelEvent(QWheelEvent *); + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + private: + ComplexToSimple *m_parent; + QRectF m_window; + QPoint m_lastMousePos; + int m_vertex; + }; +#endif + + void initEdges(); + bool calculateIntersection(int left, int right); + bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const; + QRBTree::Node *searchEdgeLeftOf(int edgeIndex) const; + QRBTree::Node *searchEdgeLeftOf(int edgeIndex, QRBTree::Node *after) const; + QPair::Node *, QRBTree::Node *> bounds(const QPodPoint &point) const; + QPair::Node *, QRBTree::Node *> outerBounds(const QPodPoint &point) const; + void splitEdgeListRange(QRBTree::Node *leftmost, QRBTree::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint); + void reorderEdgeListRange(QRBTree::Node *leftmost, QRBTree::Node *rightmost); + void sortEdgeList(const QPodPoint eventPoint); + void fillPriorityQueue(); + void calculateIntersections(); + int splitEdge(int splitIndex); + bool splitEdgesAtIntersections(); + void insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i); + void removeUnwantedEdgesAndConnect(); + void removeUnusedPoints(); + + QTriangulator *m_parent; + QDataBuffer m_edges; + QRBTree m_edgeList; + QDataBuffer m_events; + QDataBuffer m_splits; + QMaxHeap m_topIntersection; + QInt64Set m_processedEdgePairs; + int m_initialPointCount; + }; +#ifdef Q_TRIANGULATOR_DEBUG + friend class ComplexToSimple::DebugDialog; +#endif + + //=================================// + // QTriangulator::SimpleToMonotone // + //=================================// + friend class SimpleToMonotone; + class SimpleToMonotone + { + public: + inline SimpleToMonotone(QTriangulator *parent) : m_parent(parent), m_edges(0), m_upperVertex(0) { } + void decompose(); + private: + enum VertexType {MergeVertex, EndVertex, RegularVertex, StartVertex, SplitVertex}; + + struct Edge + { + QRBTree::Node *node; + int helper, twin, next, previous; + T from, to; + VertexType type; + bool pointingUp; + int upper() const {return (pointingUp ? to : from);} + int lower() const {return (pointingUp ? from : to);} + }; + + friend class CompareVertices; + class CompareVertices + { + public: + CompareVertices(SimpleToMonotone *parent) : m_parent(parent) { } + bool operator () (int i, int j) const; + private: + SimpleToMonotone *m_parent; + }; + + void setupDataStructures(); + void removeZeroLengthEdges(); + void fillPriorityQueue(); + bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const; + // Returns the rightmost edge not to the right of the given edge. + QRBTree::Node *searchEdgeLeftOfEdge(int edgeIndex) const; + // Returns the rightmost edge left of the given point. + QRBTree::Node *searchEdgeLeftOfPoint(int pointIndex) const; + void classifyVertex(int i); + void classifyVertices(); + bool pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3); + bool pointIsInSector(int vertex, int sector); + int findSector(int edge, int vertex); + void createDiagonal(int lower, int upper); + void monotoneDecomposition(); + + QTriangulator *m_parent; + QRBTree m_edgeList; + QDataBuffer m_edges; + QDataBuffer m_upperVertex; + bool m_clockwiseOrder; + }; + + //====================================// + // QTriangulator::MonotoneToTriangles // + //====================================// + friend class MonotoneToTriangles; + class MonotoneToTriangles + { + public: + inline MonotoneToTriangles(QTriangulator *parent) : m_parent(parent) { } + void decompose(); + private: + inline T indices(int index) const {return m_parent->m_indices.at(index + m_first);} + inline int next(int index) const {return (index + 1) % m_length;} + inline int previous(int index) const {return (index + m_length - 1) % m_length;} + inline bool less(int i, int j) const {return m_parent->m_vertices.at((qint32)indices(i)) < m_parent->m_vertices.at(indices(j));} + inline bool leftOfEdge(int i, int j, int k) const + { + return qPointIsLeftOfLine(m_parent->m_vertices.at((qint32)indices(i)), + m_parent->m_vertices.at((qint32)indices(j)), m_parent->m_vertices.at((qint32)indices(k))); + } + + QTriangulator *m_parent; + int m_first; + int m_length; + }; + + inline QTriangulator() : m_vertices(0) { } + + // Call this only once. + void initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix); + // Call this only once. + void initialize(const QVectorPath &path, const QTransform &matrix, qreal lod); + // Call this only once. + void initialize(const QPainterPath &path, const QTransform &matrix, qreal lod); + // Call either triangulate() or polyline() only once. + QVertexSet triangulate(); + QVertexSet polyline(); +private: + QDataBuffer m_vertices; + QVector m_indices; + uint m_hint; +}; + +//============================================================================// +// QTriangulator // +//============================================================================// + +template +QVertexSet QTriangulator::triangulate() +{ + for (int i = 0; i < m_vertices.size(); ++i) { + Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21)); + Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21)); + } + + if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill))) + m_hint |= QVectorPath::OddEvenFill; + + if (m_hint & QVectorPath::NonConvexShapeMask) { + ComplexToSimple c2s(this); + c2s.decompose(); + SimpleToMonotone s2m(this); + s2m.decompose(); + } + MonotoneToTriangles m2t(this); + m2t.decompose(); + + QVertexSet result; + result.indices = m_indices; + result.vertices.resize(2 * m_vertices.size()); + for (int i = 0; i < m_vertices.size(); ++i) { + result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE; + result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE; + } + return result; +} + +template +QVertexSet QTriangulator::polyline() +{ + for (int i = 0; i < m_vertices.size(); ++i) { + Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21)); + Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21)); + } + + if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill))) + m_hint |= QVectorPath::OddEvenFill; + + if (m_hint & QVectorPath::NonConvexShapeMask) { + ComplexToSimple c2s(this); + c2s.decompose(); + } + + QVertexSet result; + result.indices = m_indices; + result.vertices.resize(2 * m_vertices.size()); + for (int i = 0; i < m_vertices.size(); ++i) { + result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE; + result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE; + } + return result; +} + +template +void QTriangulator::initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix) +{ + m_hint = hint; + m_vertices.resize(count); + m_indices.resize(count + 1); + for (int i = 0; i < count; ++i) { + qreal x, y; + matrix.map(polygon[2 * i + 0], polygon[2 * i + 1], &x, &y); + m_vertices.at(i).x = qRound(x * Q_FIXED_POINT_SCALE); + m_vertices.at(i).y = qRound(y * Q_FIXED_POINT_SCALE); + m_indices[i] = i; + } + m_indices[count] = T(-1); //Q_TRIANGULATE_END_OF_POLYGON +} + +template +void QTriangulator::initialize(const QVectorPath &path, const QTransform &matrix, qreal lod) +{ + m_hint = path.hints(); + // Curved paths will be converted to complex polygons. + m_hint &= ~QVectorPath::CurvedShapeMask; + + const qreal *p = path.points(); + const QPainterPath::ElementType *e = path.elements(); + if (e) { + for (int i = 0; i < path.elementCount(); ++i, ++e, p += 2) { + switch (*e) { + case QPainterPath::MoveToElement: + if (!m_indices.isEmpty()) + m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON + // Fall through. + case QPainterPath::LineToElement: + m_indices.push_back(T(m_vertices.size())); + m_vertices.resize(m_vertices.size() + 1); + qreal x, y; + matrix.map(p[0], p[1], &x, &y); + m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE); + m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE); + break; + case QPainterPath::CurveToElement: + { + qreal pts[8]; + for (int i = 0; i < 4; ++i) + matrix.map(p[2 * i - 2], p[2 * i - 1], &pts[2 * i + 0], &pts[2 * i + 1]); + for (int i = 0; i < 8; ++i) + pts[i] *= lod; + QBezier bezier = QBezier::fromPoints(QPointF(pts[0], pts[1]), QPointF(pts[2], pts[3]), QPointF(pts[4], pts[5]), QPointF(pts[6], pts[7])); + QPolygonF poly = bezier.toPolygon(); + // Skip first point, it already exists in 'm_vertices'. + for (int j = 1; j < poly.size(); ++j) { + m_indices.push_back(T(m_vertices.size())); + m_vertices.resize(m_vertices.size() + 1); + m_vertices.last().x = qRound(poly.at(j).x() * Q_FIXED_POINT_SCALE / lod); + m_vertices.last().y = qRound(poly.at(j).y() * Q_FIXED_POINT_SCALE / lod); + } + } + i += 2; + e += 2; + p += 4; + break; + default: + Q_ASSERT_X(0, "QTriangulator::triangulate", "Unexpected element type."); + break; + } + } + } else { + for (int i = 0; i < path.elementCount(); ++i, p += 2) { + m_indices.push_back(T(m_vertices.size())); + m_vertices.resize(m_vertices.size() + 1); + qreal x, y; + matrix.map(p[0], p[1], &x, &y); + m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE); + m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE); + } + } + m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON +} + +template +void QTriangulator::initialize(const QPainterPath &path, const QTransform &matrix, qreal lod) +{ + initialize(qtVectorPathForPath(path), matrix, lod); +} + +//============================================================================// +// QTriangulator::ComplexToSimple // +//============================================================================// +template +void QTriangulator::ComplexToSimple::decompose() +{ + m_initialPointCount = m_parent->m_vertices.size(); + initEdges(); + do { + calculateIntersections(); + } while (splitEdgesAtIntersections()); + + removeUnwantedEdgesAndConnect(); + removeUnusedPoints(); + + m_parent->m_indices.clear(); + QBitArray processed(m_edges.size(), false); + for (int first = 0; first < m_edges.size(); ++first) { + // If already processed, or if unused path, skip. + if (processed.at(first) || m_edges.at(first).next == -1) + continue; + + int i = first; + do { + Q_ASSERT(!processed.at(i)); + Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i); + m_parent->m_indices.push_back(m_edges.at(i).from); + processed.setBit(i); + i = m_edges.at(i).next; // CCW order + } while (i != first); + m_parent->m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON + } +} + +template +void QTriangulator::ComplexToSimple::initEdges() +{ + // Initialize edge structure. + // 'next' and 'previous' are not being initialized at this point. + int first = 0; + for (int i = 0; i < m_parent->m_indices.size(); ++i) { + if (m_parent->m_indices.at(i) == T(-1)) { // Q_TRIANGULATE_END_OF_POLYGON + if (m_edges.size() != first) + m_edges.last().to = m_edges.at(first).from; + first = m_edges.size(); + } else { + Q_ASSERT(i + 1 < m_parent->m_indices.size()); + // {node, from, to, next, previous, winding, mayIntersect, pointingUp, originallyPointingUp} + Edge edge = {0, int(m_parent->m_indices.at(i)), int(m_parent->m_indices.at(i + 1)), -1, -1, 0, true, false, false}; + m_edges.add(edge); + } + } + if (first != m_edges.size()) + m_edges.last().to = m_edges.at(first).from; + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp = + m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); + } +} + +// Return true if new intersection was found +template +bool QTriangulator::ComplexToSimple::calculateIntersection(int left, int right) +{ + const Edge &e1 = m_edges.at(left); + const Edge &e2 = m_edges.at(right); + + const QPodPoint &u1 = m_parent->m_vertices.at((qint32)e1.from); + const QPodPoint &u2 = m_parent->m_vertices.at((qint32)e1.to); + const QPodPoint &v1 = m_parent->m_vertices.at((qint32)e2.from); + const QPodPoint &v2 = m_parent->m_vertices.at((qint32)e2.to); + if (qMax(u1.x, u2.x) <= qMin(v1.x, v2.x)) + return false; + + quint64 key = (left > right ? (quint64(right) << 32) | quint64(left) : (quint64(left) << 32) | quint64(right)); + if (m_processedEdgePairs.contains(key)) + return false; + m_processedEdgePairs.insert(key); + + Intersection intersection; + intersection.leftEdge = left; + intersection.rightEdge = right; + intersection.intersectionPoint = QT_PREPEND_NAMESPACE(qIntersectionPoint)(u1, u2, v1, v2); + + if (!intersection.intersectionPoint.isValid()) + return false; + + Q_ASSERT(intersection.intersectionPoint.isOnLine(u1, u2)); + Q_ASSERT(intersection.intersectionPoint.isOnLine(v1, v2)); + + intersection.vertex = m_parent->m_vertices.size(); + m_topIntersection.push(intersection); + m_parent->m_vertices.add(intersection.intersectionPoint.round()); + return true; +} + +template +bool QTriangulator::ComplexToSimple::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const +{ + const Edge &leftEdge = m_edges.at(leftEdgeIndex); + const Edge &rightEdge = m_edges.at(rightEdgeIndex); + const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper()); + const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower()); + const QPodPoint &upper = m_parent->m_vertices.at(leftEdge.upper()); + if (upper.x < qMin(l.x, u.x)) + return true; + if (upper.x > qMax(l.x, u.x)) + return false; + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(upper, l, u); + // d < 0: left, d > 0: right, d == 0: on top + if (d == 0) + d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.lower()), l, u); + return d < 0; +} + +template +QRBTree::Node *QTriangulator::ComplexToSimple::searchEdgeLeftOf(int edgeIndex) const +{ + QRBTree::Node *current = m_edgeList.root; + QRBTree::Node *result = 0; + while (current) { + if (edgeIsLeftOfEdge(edgeIndex, current->data)) { + current = current->left; + } else { + result = current; + current = current->right; + } + } + return result; +} + +template +QRBTree::Node *QTriangulator::ComplexToSimple::searchEdgeLeftOf(int edgeIndex, QRBTree::Node *after) const +{ + if (!m_edgeList.root) + return after; + QRBTree::Node *result = after; + QRBTree::Node *current = (after ? m_edgeList.next(after) : m_edgeList.front(m_edgeList.root)); + while (current) { + if (edgeIsLeftOfEdge(edgeIndex, current->data)) + return result; + result = current; + current = m_edgeList.next(current); + } + return result; +} + +template +QPair::Node *, QRBTree::Node *> QTriangulator::ComplexToSimple::bounds(const QPodPoint &point) const +{ + QRBTree::Node *current = m_edgeList.root; + QPair::Node *, QRBTree::Node *> result(0, 0); + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + if (d == 0) { + result.first = result.second = current; + break; + } + current = (d < 0 ? current->left : current->right); + } + if (current == 0) + return result; + + current = result.first->left; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + Q_ASSERT(d >= 0); + if (d == 0) { + result.first = current; + current = current->left; + } else { + current = current->right; + } + } + + current = result.second->right; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + Q_ASSERT(d <= 0); + if (d == 0) { + result.second = current; + current = current->right; + } else { + current = current->left; + } + } + + return result; +} + +template +QPair::Node *, QRBTree::Node *> QTriangulator::ComplexToSimple::outerBounds(const QPodPoint &point) const +{ + QRBTree::Node *current = m_edgeList.root; + QPair::Node *, QRBTree::Node *> result(0, 0); + + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + if (d == 0) + break; + if (d < 0) { + result.second = current; + current = current->left; + } else { + result.first = current; + current = current->right; + } + } + + if (!current) + return result; + + QRBTree::Node *mid = current; + + current = mid->left; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + Q_ASSERT(d >= 0); + if (d == 0) { + current = current->left; + } else { + result.first = current; + current = current->right; + } + } + + current = mid->right; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2); + Q_ASSERT(d <= 0); + if (d == 0) { + current = current->right; + } else { + result.second = current; + current = current->left; + } + } + + return result; +} + +template +void QTriangulator::ComplexToSimple::splitEdgeListRange(QRBTree::Node *leftmost, QRBTree::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint) +{ + Q_ASSERT(leftmost && rightmost); + + // Split. + for (;;) { + const QPodPoint &u = m_parent->m_vertices.at(m_edges.at(leftmost->data).from); + const QPodPoint &v = m_parent->m_vertices.at(m_edges.at(leftmost->data).to); + Q_ASSERT(intersectionPoint.isOnLine(u, v)); + const Split split = {vertex, leftmost->data, intersectionPoint.isAccurate()}; + if (intersectionPoint.xOffset.numerator != 0 || intersectionPoint.yOffset.numerator != 0 || (intersectionPoint.upperLeft != u && intersectionPoint.upperLeft != v)) + m_splits.add(split); + if (leftmost == rightmost) + break; + leftmost = m_edgeList.next(leftmost); + } +} + +template +void QTriangulator::ComplexToSimple::reorderEdgeListRange(QRBTree::Node *leftmost, QRBTree::Node *rightmost) +{ + Q_ASSERT(leftmost && rightmost); + + QRBTree::Node *storeLeftmost = leftmost; + QRBTree::Node *storeRightmost = rightmost; + + // Reorder. + while (leftmost != rightmost) { + Edge &left = m_edges.at(leftmost->data); + Edge &right = m_edges.at(rightmost->data); + qSwap(left.node, right.node); + qSwap(leftmost->data, rightmost->data); + leftmost = m_edgeList.next(leftmost); + if (leftmost == rightmost) + break; + rightmost = m_edgeList.previous(rightmost); + } + + rightmost = m_edgeList.next(storeRightmost); + leftmost = m_edgeList.previous(storeLeftmost); + if (leftmost) + calculateIntersection(leftmost->data, storeLeftmost->data); + if (rightmost) + calculateIntersection(storeRightmost->data, rightmost->data); +} + +template +void QTriangulator::ComplexToSimple::sortEdgeList(const QPodPoint eventPoint) +{ + QIntersectionPoint eventPoint2 = QT_PREPEND_NAMESPACE(qIntersectionPoint)(eventPoint); + while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint < eventPoint2) { + Intersection intersection = m_topIntersection.pop(); + + QIntersectionPoint currentIntersectionPoint = intersection.intersectionPoint; + int currentVertex = intersection.vertex; + + QRBTree::Node *leftmost = m_edges.at(intersection.leftEdge).node; + QRBTree::Node *rightmost = m_edges.at(intersection.rightEdge).node; + + for (;;) { + QRBTree::Node *previous = m_edgeList.previous(leftmost); + if (!previous) + break; + const Edge &edge = m_edges.at(previous->data); + const QPodPoint &u = m_parent->m_vertices.at((qint32)edge.from); + const QPodPoint &v = m_parent->m_vertices.at((qint32)edge.to); + if (!currentIntersectionPoint.isOnLine(u, v)) { + Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0); + break; + } + leftmost = previous; + } + + for (;;) { + QRBTree::Node *next = m_edgeList.next(rightmost); + if (!next) + break; + const Edge &edge = m_edges.at(next->data); + const QPodPoint &u = m_parent->m_vertices.at((qint32)edge.from); + const QPodPoint &v = m_parent->m_vertices.at((qint32)edge.to); + if (!currentIntersectionPoint.isOnLine(u, v)) { + Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0); + break; + } + rightmost = next; + } + + Q_ASSERT(leftmost && rightmost); + splitEdgeListRange(leftmost, rightmost, currentVertex, currentIntersectionPoint); + reorderEdgeListRange(leftmost, rightmost); + + while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= currentIntersectionPoint) + m_topIntersection.pop(); + +#ifdef Q_TRIANGULATOR_DEBUG + DebugDialog dialog(this, intersection.vertex); + dialog.exec(); +#endif + + } +} + +template +void QTriangulator::ComplexToSimple::fillPriorityQueue() +{ + m_events.reset(); + m_events.reserve(m_edges.size() * 2); + for (int i = 0; i < m_edges.size(); ++i) { + Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1); + Q_ASSERT(m_edges.at(i).node == 0); + Q_ASSERT(m_edges.at(i).pointingUp == m_edges.at(i).originallyPointingUp); + Q_ASSERT(m_edges.at(i).pointingUp == (m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from))); + // Ignore zero-length edges. + if (m_parent->m_vertices.at(m_edges.at(i).to) != m_parent->m_vertices.at(m_edges.at(i).from)) { + QPodPoint upper = m_parent->m_vertices.at(m_edges.at(i).upper()); + QPodPoint lower = m_parent->m_vertices.at(m_edges.at(i).lower()); + Event upperEvent = {{upper.x, upper.y}, Event::Upper, i}; + Event lowerEvent = {{lower.x, lower.y}, Event::Lower, i}; + m_events.add(upperEvent); + m_events.add(lowerEvent); + } + } + + std::sort(m_events.data(), m_events.data() + m_events.size()); +} + +template +void QTriangulator::ComplexToSimple::calculateIntersections() +{ + fillPriorityQueue(); + + Q_ASSERT(m_topIntersection.empty()); + Q_ASSERT(m_edgeList.root == 0); + + // Find all intersection points. + while (!m_events.isEmpty()) { + Event event = m_events.last(); + sortEdgeList(event.point); + + // Find all edges in the edge list that contain the current vertex and mark them to be split later. + QPair::Node *, QRBTree::Node *> range = bounds(event.point); + QRBTree::Node *leftNode = range.first ? m_edgeList.previous(range.first) : 0; + int vertex = (event.type == Event::Upper ? m_edges.at(event.edge).upper() : m_edges.at(event.edge).lower()); + QIntersectionPoint eventPoint = QT_PREPEND_NAMESPACE(qIntersectionPoint)(event.point); + + if (range.first != 0) { + splitEdgeListRange(range.first, range.second, vertex, eventPoint); + reorderEdgeListRange(range.first, range.second); + } + + // Handle the edges with start or end point in the current vertex. + while (!m_events.isEmpty() && m_events.last().point == event.point) { + event = m_events.last(); + m_events.pop_back(); + int i = event.edge; + + if (m_edges.at(i).node) { + // Remove edge from edge list. + Q_ASSERT(event.type == Event::Lower); + QRBTree::Node *left = m_edgeList.previous(m_edges.at(i).node); + QRBTree::Node *right = m_edgeList.next(m_edges.at(i).node); + m_edgeList.deleteNode(m_edges.at(i).node); + if (!left || !right) + continue; + calculateIntersection(left->data, right->data); + } else { + // Insert edge into edge list. + Q_ASSERT(event.type == Event::Upper); + QRBTree::Node *left = searchEdgeLeftOf(i, leftNode); + m_edgeList.attachAfter(left, m_edges.at(i).node = m_edgeList.newNode()); + m_edges.at(i).node->data = i; + QRBTree::Node *right = m_edgeList.next(m_edges.at(i).node); + if (left) + calculateIntersection(left->data, i); + if (right) + calculateIntersection(i, right->data); + } + } + while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= eventPoint) + m_topIntersection.pop(); +#ifdef Q_TRIANGULATOR_DEBUG + DebugDialog dialog(this, vertex); + dialog.exec(); +#endif + } + m_processedEdgePairs.clear(); +} + +// Split an edge into two pieces at the given point. +// The upper piece is pushed to the end of the 'm_edges' vector. +// The lower piece replaces the old edge. +// Return the edge whose 'from' is 'pointIndex'. +template +int QTriangulator::ComplexToSimple::splitEdge(int splitIndex) +{ + const Split &split = m_splits.at(splitIndex); + Edge &lowerEdge = m_edges.at(split.edge); + Q_ASSERT(lowerEdge.node == 0); + Q_ASSERT(lowerEdge.previous == -1 && lowerEdge.next == -1); + + if (lowerEdge.from == split.vertex) + return split.edge; + if (lowerEdge.to == split.vertex) + return lowerEdge.next; + + // Check that angle >= 90 degrees. + //Q_ASSERT(qDot(m_points.at(m_edges.at(edgeIndex).from) - m_points.at(pointIndex), + // m_points.at(m_edges.at(edgeIndex).to) - m_points.at(pointIndex)) <= 0); + + Edge upperEdge = lowerEdge; + upperEdge.mayIntersect |= !split.accurate; // The edge may have been split before at an inaccurate split point. + lowerEdge.mayIntersect = !split.accurate; + if (lowerEdge.pointingUp) { + lowerEdge.to = upperEdge.from = split.vertex; + m_edges.add(upperEdge); + return m_edges.size() - 1; + } else { + lowerEdge.from = upperEdge.to = split.vertex; + m_edges.add(upperEdge); + return split.edge; + } +} + +template +bool QTriangulator::ComplexToSimple::splitEdgesAtIntersections() +{ + for (int i = 0; i < m_edges.size(); ++i) + m_edges.at(i).mayIntersect = false; + bool checkForNewIntersections = false; + for (int i = 0; i < m_splits.size(); ++i) { + splitEdge(i); + checkForNewIntersections |= !m_splits.at(i).accurate; + } + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp = + m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); + } + m_splits.reset(); + return checkForNewIntersections; +} + +template +void QTriangulator::ComplexToSimple::insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i) +{ + // Edges with zero length should not reach this part. + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(i).from) != m_parent->m_vertices.at(m_edges.at(i).to)); + + // Skip edges with unwanted winding number. + int windingNumber = m_edges.at(i).winding; + if (m_edges.at(i).originallyPointingUp) + ++windingNumber; + + // Make sure exactly one fill rule is specified. + Q_ASSERT(((m_parent->m_hint & QVectorPath::WindingFill) != 0) != ((m_parent->m_hint & QVectorPath::OddEvenFill) != 0)); + + if ((m_parent->m_hint & QVectorPath::WindingFill) && windingNumber != 0 && windingNumber != 1) + return; + + // Skip cancelling edges. + if (!orderedEdges.isEmpty()) { + int j = orderedEdges[orderedEdges.size() - 1]; + // If the last edge is already connected in one end, it should not be cancelled. + if (m_edges.at(j).next == -1 && m_edges.at(j).previous == -1 + && (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(j).to)) + && (m_parent->m_vertices.at(m_edges.at(i).to) == m_parent->m_vertices.at(m_edges.at(j).from))) { + orderedEdges.removeLast(); + return; + } + } + orderedEdges.append(i); +} + +template +void QTriangulator::ComplexToSimple::removeUnwantedEdgesAndConnect() +{ + Q_ASSERT(m_edgeList.root == 0); + // Initialize priority queue. + fillPriorityQueue(); + + ShortArray orderedEdges; + + while (!m_events.isEmpty()) { + Event event = m_events.last(); + int edgeIndex = event.edge; + + // Check that all the edges in the list crosses the current scanline + //if (m_edgeList.root) { + // for (QRBTree::Node *node = m_edgeList.front(m_edgeList.root); node; node = m_edgeList.next(node)) { + // Q_ASSERT(event.point <= m_points.at(m_edges.at(node->data).lower())); + // } + //} + + orderedEdges.clear(); + QPair::Node *, QRBTree::Node *> b = outerBounds(event.point); + if (m_edgeList.root) { + QRBTree::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root)); + // Process edges that are going to be removed from the edge list at the current event point. + while (current != b.second) { + Q_ASSERT(current); + Q_ASSERT(m_edges.at(current->data).node == current); + Q_ASSERT(QT_PREPEND_NAMESPACE(qIntersectionPoint)(event.point).isOnLine(m_parent->m_vertices.at(m_edges.at(current->data).from), m_parent->m_vertices.at(m_edges.at(current->data).to))); + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(current->data).from) == event.point || m_parent->m_vertices.at(m_edges.at(current->data).to) == event.point); + insertEdgeIntoVectorIfWanted(orderedEdges, current->data); + current = m_edgeList.next(current); + } + } + + // Remove edges above the event point, insert edges below the event point. + do { + event = m_events.last(); + m_events.pop_back(); + edgeIndex = event.edge; + + // Edges with zero length should not reach this part. + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(edgeIndex).from) != m_parent->m_vertices.at(m_edges.at(edgeIndex).to)); + + if (m_edges.at(edgeIndex).node) { + Q_ASSERT(event.type == Event::Lower); + Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).lower())); + m_edgeList.deleteNode(m_edges.at(edgeIndex).node); + } else { + Q_ASSERT(event.type == Event::Upper); + Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).upper())); + QRBTree::Node *left = searchEdgeLeftOf(edgeIndex, b.first); + m_edgeList.attachAfter(left, m_edges.at(edgeIndex).node = m_edgeList.newNode()); + m_edges.at(edgeIndex).node->data = edgeIndex; + } + } while (!m_events.isEmpty() && m_events.last().point == event.point); + + if (m_edgeList.root) { + QRBTree::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root)); + + // Calculate winding number and turn counter-clockwise. + int currentWindingNumber = (b.first ? m_edges.at(b.first->data).winding : 0); + while (current != b.second) { + Q_ASSERT(current); + //Q_ASSERT(b.second == 0 || m_edgeList.order(current, b.second) < 0); + int i = current->data; + Q_ASSERT(m_edges.at(i).node == current); + + // Winding number. + int ccwWindingNumber = m_edges.at(i).winding = currentWindingNumber; + if (m_edges.at(i).originallyPointingUp) { + --m_edges.at(i).winding; + } else { + ++m_edges.at(i).winding; + ++ccwWindingNumber; + } + currentWindingNumber = m_edges.at(i).winding; + + // Turn counter-clockwise. + if ((ccwWindingNumber & 1) == 0) { + Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1); + qSwap(m_edges.at(i).from, m_edges.at(i).to); + m_edges.at(i).pointingUp = !m_edges.at(i).pointingUp; + } + + current = m_edgeList.next(current); + } + + // Process edges that were inserted into the edge list at the current event point. + current = (b.second ? m_edgeList.previous(b.second) : m_edgeList.back(m_edgeList.root)); + while (current != b.first) { + Q_ASSERT(current); + Q_ASSERT(m_edges.at(current->data).node == current); + insertEdgeIntoVectorIfWanted(orderedEdges, current->data); + current = m_edgeList.previous(current); + } + } + if (orderedEdges.isEmpty()) + continue; + + Q_ASSERT((orderedEdges.size() & 1) == 0); + + // Connect edges. + // First make sure the first edge point towards the current point. + int i; + if (m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).from) == event.point) { + i = 1; + int copy = orderedEdges[0]; // Make copy in case the append() will cause a reallocation. + orderedEdges.append(copy); + } else { + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).to) == event.point); + i = 0; + } + + // Remove references to duplicate points. First find the point with lowest index. + int pointIndex = INT_MAX; + for (int j = i; j < orderedEdges.size(); j += 2) { + Q_ASSERT(j + 1 < orderedEdges.size()); + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j]).to) == event.point); + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j + 1]).from) == event.point); + if (m_edges.at(orderedEdges[j]).to < pointIndex) + pointIndex = m_edges.at(orderedEdges[j]).to; + if (m_edges.at(orderedEdges[j + 1]).from < pointIndex) + pointIndex = m_edges.at(orderedEdges[j + 1]).from; + } + + for (; i < orderedEdges.size(); i += 2) { + // Remove references to duplicate points by making all edges reference one common point. + m_edges.at(orderedEdges[i]).to = m_edges.at(orderedEdges[i + 1]).from = pointIndex; + + Q_ASSERT(m_edges.at(orderedEdges[i]).pointingUp || m_edges.at(orderedEdges[i]).previous != -1); + Q_ASSERT(!m_edges.at(orderedEdges[i + 1]).pointingUp || m_edges.at(orderedEdges[i + 1]).next != -1); + + m_edges.at(orderedEdges[i]).next = orderedEdges[i + 1]; + m_edges.at(orderedEdges[i + 1]).previous = orderedEdges[i]; + } + } // end while +} + +template +void QTriangulator::ComplexToSimple::removeUnusedPoints() { + QBitArray used(m_parent->m_vertices.size(), false); + for (int i = 0; i < m_edges.size(); ++i) { + Q_ASSERT((m_edges.at(i).previous == -1) == (m_edges.at(i).next == -1)); + if (m_edges.at(i).next != -1) + used.setBit(m_edges.at(i).from); + } + QDataBuffer newMapping(m_parent->m_vertices.size()); + newMapping.resize(m_parent->m_vertices.size()); + int count = 0; + for (int i = 0; i < m_parent->m_vertices.size(); ++i) { + if (used.at(i)) { + m_parent->m_vertices.at(count) = m_parent->m_vertices.at(i); + newMapping.at(i) = count; + ++count; + } + } + m_parent->m_vertices.resize(count); + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).from = newMapping.at(m_edges.at(i).from); + m_edges.at(i).to = newMapping.at(m_edges.at(i).to); + } +} + +template +inline bool QTriangulator::ComplexToSimple::Event::operator < (const Event &other) const +{ + if (point == other.point) + return type < other.type; // 'Lower' has higher priority than 'Upper'. + return other.point < point; +} + +//============================================================================// +// QTriangulator::ComplexToSimple::DebugDialog // +//============================================================================// + +#ifdef Q_TRIANGULATOR_DEBUG +template +QTriangulator::ComplexToSimple::DebugDialog::DebugDialog(ComplexToSimple *parent, int currentVertex) + : m_parent(parent), m_vertex(currentVertex) +{ + QDataBuffer &vertices = m_parent->m_parent->m_vertices; + if (vertices.isEmpty()) + return; + + int minX, maxX, minY, maxY; + minX = maxX = vertices.at(0).x; + minY = maxY = vertices.at(0).y; + for (int i = 1; i < vertices.size(); ++i) { + minX = qMin(minX, vertices.at(i).x); + maxX = qMax(maxX, vertices.at(i).x); + minY = qMin(minY, vertices.at(i).y); + maxY = qMax(maxY, vertices.at(i).y); + } + int w = maxX - minX; + int h = maxY - minY; + qreal border = qMin(w, h) / 10.0; + m_window = QRectF(minX - border, minY - border, (maxX - minX + 2 * border), (maxY - minY + 2 * border)); +} + +template +void QTriangulator::ComplexToSimple::DebugDialog::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing, true); + p.fillRect(rect(), Qt::black); + QDataBuffer &vertices = m_parent->m_parent->m_vertices; + if (vertices.isEmpty()) + return; + + qreal halfPointSize = qMin(m_window.width(), m_window.height()) / 300.0; + p.setWindow(m_window.toRect()); + + p.setPen(Qt::white); + + QDataBuffer &edges = m_parent->m_edges; + for (int i = 0; i < edges.size(); ++i) { + QPodPoint u = vertices.at(edges.at(i).from); + QPodPoint v = vertices.at(edges.at(i).to); + p.drawLine(u.x, u.y, v.x, v.y); + } + + for (int i = 0; i < vertices.size(); ++i) { + QPodPoint q = vertices.at(i); + p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::red); + } + + Qt::GlobalColor colors[6] = {Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow}; + p.setOpacity(0.5); + int count = 0; + if (m_parent->m_edgeList.root) { + QRBTree::Node *current = m_parent->m_edgeList.front(m_parent->m_edgeList.root); + while (current) { + p.setPen(colors[count++ % 6]); + QPodPoint u = vertices.at(edges.at(current->data).from); + QPodPoint v = vertices.at(edges.at(current->data).to); + p.drawLine(u.x, u.y, v.x, v.y); + current = m_parent->m_edgeList.next(current); + } + } + + p.setOpacity(1.0); + QPodPoint q = vertices.at(m_vertex); + p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::green); + + p.setPen(Qt::gray); + QDataBuffer &splits = m_parent->m_splits; + for (int i = 0; i < splits.size(); ++i) { + QPodPoint q = vertices.at(splits.at(i).vertex); + QPodPoint u = vertices.at(edges.at(splits.at(i).edge).from) - q; + QPodPoint v = vertices.at(edges.at(splits.at(i).edge).to) - q; + qreal uLen = qSqrt(qDot(u, u)); + qreal vLen = qSqrt(qDot(v, v)); + if (uLen) { + u.x *= 2 * halfPointSize / uLen; + u.y *= 2 * halfPointSize / uLen; + } + if (vLen) { + v.x *= 2 * halfPointSize / vLen; + v.y *= 2 * halfPointSize / vLen; + } + u += q; + v += q; + p.drawLine(u.x, u.y, v.x, v.y); + } +} + +template +void QTriangulator::ComplexToSimple::DebugDialog::wheelEvent(QWheelEvent *event) +{ + qreal scale = qExp(-0.001 * event->delta()); + QPointF center = m_window.center(); + QPointF delta = scale * (m_window.bottomRight() - center); + m_window = QRectF(center - delta, center + delta); + event->accept(); + update(); +} + +template +void QTriangulator::ComplexToSimple::DebugDialog::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + QPointF delta = event->pos() - m_lastMousePos; + delta.setX(delta.x() * m_window.width() / width()); + delta.setY(delta.y() * m_window.height() / height()); + m_window.translate(-delta.x(), -delta.y()); + m_lastMousePos = event->pos(); + event->accept(); + update(); + } +} + +template +void QTriangulator::ComplexToSimple::DebugDialog::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + m_lastMousePos = event->pos(); + event->accept(); +} + + +#endif + +//============================================================================// +// QTriangulator::SimpleToMonotone // +//============================================================================// +template +void QTriangulator::SimpleToMonotone::decompose() +{ + setupDataStructures(); + removeZeroLengthEdges(); + monotoneDecomposition(); + + m_parent->m_indices.clear(); + QBitArray processed(m_edges.size(), false); + for (int first = 0; first < m_edges.size(); ++first) { + if (processed.at(first)) + continue; + int i = first; + do { + Q_ASSERT(!processed.at(i)); + Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i); + m_parent->m_indices.push_back(m_edges.at(i).from); + processed.setBit(i); + i = m_edges.at(i).next; + } while (i != first); + if (m_parent->m_indices.size() > 0 && m_parent->m_indices.back() != T(-1)) // Q_TRIANGULATE_END_OF_POLYGON + m_parent->m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON + } +} + +template +void QTriangulator::SimpleToMonotone::setupDataStructures() +{ + int i = 0; + Edge e; + e.node = 0; + e.twin = -1; + + while (i + 3 <= m_parent->m_indices.size()) { + int start = m_edges.size(); + + do { + e.from = m_parent->m_indices.at(i); + e.type = RegularVertex; + e.next = m_edges.size() + 1; + e.previous = m_edges.size() - 1; + m_edges.add(e); + ++i; + Q_ASSERT(i < m_parent->m_indices.size()); + } while (m_parent->m_indices.at(i) != T(-1)); // Q_TRIANGULATE_END_OF_POLYGON + + m_edges.last().next = start; + m_edges.at(start).previous = m_edges.size() - 1; + ++i; // Skip Q_TRIANGULATE_END_OF_POLYGON. + } + + for (i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).to = m_edges.at(m_edges.at(i).next).from; + m_edges.at(i).pointingUp = m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); + m_edges.at(i).helper = -1; // Not initialized here. + } +} + +template +void QTriangulator::SimpleToMonotone::removeZeroLengthEdges() +{ + for (int i = 0; i < m_edges.size(); ++i) { + if (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(i).to)) { + m_edges.at(m_edges.at(i).previous).next = m_edges.at(i).next; + m_edges.at(m_edges.at(i).next).previous = m_edges.at(i).previous; + m_edges.at(m_edges.at(i).next).from = m_edges.at(i).from; + m_edges.at(i).next = -1; // Mark as removed. + } + } + + QDataBuffer newMapping(m_edges.size()); + newMapping.resize(m_edges.size()); + int count = 0; + for (int i = 0; i < m_edges.size(); ++i) { + if (m_edges.at(i).next != -1) { + m_edges.at(count) = m_edges.at(i); + newMapping.at(i) = count; + ++count; + } + } + m_edges.resize(count); + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).next = newMapping.at(m_edges.at(i).next); + m_edges.at(i).previous = newMapping.at(m_edges.at(i).previous); + } +} + +template +void QTriangulator::SimpleToMonotone::fillPriorityQueue() +{ + m_upperVertex.reset(); + m_upperVertex.reserve(m_edges.size()); + for (int i = 0; i < m_edges.size(); ++i) + m_upperVertex.add(i); + CompareVertices cmp(this); + std::sort(m_upperVertex.data(), m_upperVertex.data() + m_upperVertex.size(), cmp); + //for (int i = 1; i < m_upperVertex.size(); ++i) { + // Q_ASSERT(!cmp(m_upperVertex.at(i), m_upperVertex.at(i - 1))); + //} +} + +template +bool QTriangulator::SimpleToMonotone::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const +{ + const Edge &leftEdge = m_edges.at(leftEdgeIndex); + const Edge &rightEdge = m_edges.at(rightEdgeIndex); + const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper()); + const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.upper()), l, u); + // d < 0: left, d > 0: right, d == 0: on top + if (d == 0) + d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.lower()), l, u); + return d < 0; +} + +// Returns the rightmost edge not to the right of the given edge. +template +QRBTree::Node *QTriangulator::SimpleToMonotone::searchEdgeLeftOfEdge(int edgeIndex) const +{ + QRBTree::Node *current = m_edgeList.root; + QRBTree::Node *result = 0; + while (current) { + if (edgeIsLeftOfEdge(edgeIndex, current->data)) { + current = current->left; + } else { + result = current; + current = current->right; + } + } + return result; +} + +// Returns the rightmost edge left of the given point. +template +QRBTree::Node *QTriangulator::SimpleToMonotone::searchEdgeLeftOfPoint(int pointIndex) const +{ + QRBTree::Node *current = m_edgeList.root; + QRBTree::Node *result = 0; + while (current) { + const QPodPoint &p1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &p2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(pointIndex), p1, p2); + if (d <= 0) { + current = current->left; + } else { + result = current; + current = current->right; + } + } + return result; +} + +template +void QTriangulator::SimpleToMonotone::classifyVertex(int i) +{ + Edge &e2 = m_edges.at(i); + const Edge &e1 = m_edges.at(e2.previous); + + bool startOrSplit = (e1.pointingUp && !e2.pointingUp); + bool endOrMerge = (!e1.pointingUp && e2.pointingUp); + + const QPodPoint &p1 = m_parent->m_vertices.at(e1.from); + const QPodPoint &p2 = m_parent->m_vertices.at(e2.from); + const QPodPoint &p3 = m_parent->m_vertices.at(e2.to); + qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(p1, p2, p3); + Q_ASSERT(d != 0 || (!startOrSplit && !endOrMerge)); + + e2.type = RegularVertex; + + if (m_clockwiseOrder) { + if (startOrSplit) + e2.type = (d < 0 ? SplitVertex : StartVertex); + else if (endOrMerge) + e2.type = (d < 0 ? MergeVertex : EndVertex); + } else { + if (startOrSplit) + e2.type = (d > 0 ? SplitVertex : StartVertex); + else if (endOrMerge) + e2.type = (d > 0 ? MergeVertex : EndVertex); + } +} + +template +void QTriangulator::SimpleToMonotone::classifyVertices() +{ + for (int i = 0; i < m_edges.size(); ++i) + classifyVertex(i); +} + +template +bool QTriangulator::SimpleToMonotone::pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3) +{ + bool leftOfPreviousEdge = !qPointIsLeftOfLine(p, v2, v1); + bool leftOfNextEdge = !qPointIsLeftOfLine(p, v3, v2); + + if (qPointIsLeftOfLine(v1, v2, v3)) + return leftOfPreviousEdge && leftOfNextEdge; + else + return leftOfPreviousEdge || leftOfNextEdge; +} + +template +bool QTriangulator::SimpleToMonotone::pointIsInSector(int vertex, int sector) +{ + const QPodPoint ¢er = m_parent->m_vertices.at(m_edges.at(sector).from); + // Handle degenerate edges. + while (m_parent->m_vertices.at(m_edges.at(vertex).from) == center) + vertex = m_edges.at(vertex).next; + int next = m_edges.at(sector).next; + while (m_parent->m_vertices.at(m_edges.at(next).from) == center) + next = m_edges.at(next).next; + int previous = m_edges.at(sector).previous; + while (m_parent->m_vertices.at(m_edges.at(previous).from) == center) + previous = m_edges.at(previous).previous; + + const QPodPoint &p = m_parent->m_vertices.at(m_edges.at(vertex).from); + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(previous).from); + const QPodPoint &v3 = m_parent->m_vertices.at(m_edges.at(next).from); + if (m_clockwiseOrder) + return pointIsInSector(p, v3, center, v1); + else + return pointIsInSector(p, v1, center, v3); +} + +template +int QTriangulator::SimpleToMonotone::findSector(int edge, int vertex) +{ + while (!pointIsInSector(vertex, edge)) { + edge = m_edges.at(m_edges.at(edge).previous).twin; + Q_ASSERT(edge != -1); + } + return edge; +} + +template +void QTriangulator::SimpleToMonotone::createDiagonal(int lower, int upper) +{ + lower = findSector(lower, upper); + upper = findSector(upper, lower); + + int prevLower = m_edges.at(lower).previous; + int prevUpper = m_edges.at(upper).previous; + + Edge e; + + e.twin = m_edges.size() + 1; + e.next = upper; + e.previous = prevLower; + e.from = m_edges.at(lower).from; + e.to = m_edges.at(upper).from; + m_edges.at(upper).previous = m_edges.at(prevLower).next = int(m_edges.size()); + m_edges.add(e); + + e.twin = m_edges.size() - 1; + e.next = lower; + e.previous = prevUpper; + e.from = m_edges.at(upper).from; + e.to = m_edges.at(lower).from; + m_edges.at(lower).previous = m_edges.at(prevUpper).next = int(m_edges.size()); + m_edges.add(e); +} + +template +void QTriangulator::SimpleToMonotone::monotoneDecomposition() +{ + if (m_edges.isEmpty()) + return; + + Q_ASSERT(!m_edgeList.root); + QDataBuffer > diagonals(m_upperVertex.size()); + + int i = 0; + for (int index = 1; index < m_edges.size(); ++index) { + if (m_parent->m_vertices.at(m_edges.at(index).from) < m_parent->m_vertices.at(m_edges.at(i).from)) + i = index; + } + Q_ASSERT(i < m_edges.size()); + int j = m_edges.at(i).previous; + Q_ASSERT(j < m_edges.size()); + m_clockwiseOrder = qPointIsLeftOfLine(m_parent->m_vertices.at((quint32)m_edges.at(i).from), + m_parent->m_vertices.at((quint32)m_edges.at(j).from), m_parent->m_vertices.at((quint32)m_edges.at(i).to)); + + classifyVertices(); + fillPriorityQueue(); + + // debug: set helpers explicitly (shouldn't be necessary) + //for (int i = 0; i < m_edges.size(); ++i) + // m_edges.at(i).helper = m_edges.at(i).upper(); + + while (!m_upperVertex.isEmpty()) { + i = m_upperVertex.last(); + Q_ASSERT(i < m_edges.size()); + m_upperVertex.pop_back(); + j = m_edges.at(i).previous; + Q_ASSERT(j < m_edges.size()); + + QRBTree::Node *leftEdgeNode = 0; + + switch (m_edges.at(i).type) { + case RegularVertex: + // If polygon interior is to the right of the vertex... + if (m_edges.at(i).pointingUp == m_clockwiseOrder) { + if (m_edges.at(i).node) { + Q_ASSERT(!m_edges.at(j).node); + if (m_edges.at(m_edges.at(i).helper).type == MergeVertex) + diagonals.add(QPair(i, m_edges.at(i).helper)); + m_edges.at(j).node = m_edges.at(i).node; + m_edges.at(i).node = 0; + m_edges.at(j).node->data = j; + m_edges.at(j).helper = i; + } else if (m_edges.at(j).node) { + Q_ASSERT(!m_edges.at(i).node); + if (m_edges.at(m_edges.at(j).helper).type == MergeVertex) + diagonals.add(QPair(i, m_edges.at(j).helper)); + m_edges.at(i).node = m_edges.at(j).node; + m_edges.at(j).node = 0; + m_edges.at(i).node->data = i; + m_edges.at(i).helper = i; + } else { + qWarning("Inconsistent polygon. (#1)"); + } + } else { + leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); + if (leftEdgeNode) { + if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex) + diagonals.add(QPair(i, m_edges.at(leftEdgeNode->data).helper)); + m_edges.at(leftEdgeNode->data).helper = i; + } else { + qWarning("Inconsistent polygon. (#2)"); + } + } + break; + case SplitVertex: + leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); + if (leftEdgeNode) { + diagonals.add(QPair(i, m_edges.at(leftEdgeNode->data).helper)); + m_edges.at(leftEdgeNode->data).helper = i; + } else { + qWarning("Inconsistent polygon. (#3)"); + } + // Fall through. + case StartVertex: + if (m_clockwiseOrder) { + leftEdgeNode = searchEdgeLeftOfEdge(j); + QRBTree::Node *node = m_edgeList.newNode(); + node->data = j; + m_edges.at(j).node = node; + m_edges.at(j).helper = i; + m_edgeList.attachAfter(leftEdgeNode, node); + Q_ASSERT(m_edgeList.validate()); + } else { + leftEdgeNode = searchEdgeLeftOfEdge(i); + QRBTree::Node *node = m_edgeList.newNode(); + node->data = i; + m_edges.at(i).node = node; + m_edges.at(i).helper = i; + m_edgeList.attachAfter(leftEdgeNode, node); + Q_ASSERT(m_edgeList.validate()); + } + break; + case MergeVertex: + leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); + if (leftEdgeNode) { + if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex) + diagonals.add(QPair(i, m_edges.at(leftEdgeNode->data).helper)); + m_edges.at(leftEdgeNode->data).helper = i; + } else { + qWarning("Inconsistent polygon. (#4)"); + } + // Fall through. + case EndVertex: + if (m_clockwiseOrder) { + if (m_edges.at(m_edges.at(i).helper).type == MergeVertex) + diagonals.add(QPair(i, m_edges.at(i).helper)); + if (m_edges.at(i).node) { + m_edgeList.deleteNode(m_edges.at(i).node); + Q_ASSERT(m_edgeList.validate()); + } else { + qWarning("Inconsistent polygon. (#5)"); + } + } else { + if (m_edges.at(m_edges.at(j).helper).type == MergeVertex) + diagonals.add(QPair(i, m_edges.at(j).helper)); + if (m_edges.at(j).node) { + m_edgeList.deleteNode(m_edges.at(j).node); + Q_ASSERT(m_edgeList.validate()); + } else { + qWarning("Inconsistent polygon. (#6)"); + } + } + break; + } + } + + for (int i = 0; i < diagonals.size(); ++i) + createDiagonal(diagonals.at(i).first, diagonals.at(i).second); +} + +template +bool QTriangulator::SimpleToMonotone::CompareVertices::operator () (int i, int j) const +{ + if (m_parent->m_edges.at(i).from == m_parent->m_edges.at(j).from) + return m_parent->m_edges.at(i).type > m_parent->m_edges.at(j).type; + return m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).from) > + m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).from); +} + +//============================================================================// +// QTriangulator::MonotoneToTriangles // +//============================================================================// +template +void QTriangulator::MonotoneToTriangles::decompose() +{ + QVector result; + QDataBuffer stack(m_parent->m_indices.size()); + m_first = 0; + // Require at least three more indices. + while (m_first + 3 <= m_parent->m_indices.size()) { + m_length = 0; + while (m_parent->m_indices.at(m_first + m_length) != T(-1)) { // Q_TRIANGULATE_END_OF_POLYGON + ++m_length; + Q_ASSERT(m_first + m_length < m_parent->m_indices.size()); + } + if (m_length < 3) { + m_first += m_length + 1; + continue; + } + + int minimum = 0; + while (less(next(minimum), minimum)) + minimum = next(minimum); + while (less(previous(minimum), minimum)) + minimum = previous(minimum); + + stack.reset(); + stack.add(minimum); + int left = previous(minimum); + int right = next(minimum); + bool stackIsOnLeftSide; + bool clockwiseOrder = leftOfEdge(minimum, left, right); + + if (less(left, right)) { + stack.add(left); + left = previous(left); + stackIsOnLeftSide = true; + } else { + stack.add(right); + right = next(right); + stackIsOnLeftSide = false; + } + + for (int count = 0; count + 2 < m_length; ++count) + { + Q_ASSERT(stack.size() >= 2); + if (less(left, right)) { + if (stackIsOnLeftSide == false) { + for (int i = 0; i + 1 < stack.size(); ++i) { + result.push_back(indices(stack.at(i + 1))); + result.push_back(indices(left)); + result.push_back(indices(stack.at(i))); + } + stack.first() = stack.last(); + stack.resize(1); + } else { + while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(left, stack.at(stack.size() - 2), stack.last()))) { + result.push_back(indices(stack.at(stack.size() - 2))); + result.push_back(indices(left)); + result.push_back(indices(stack.last())); + stack.pop_back(); + } + } + stack.add(left); + left = previous(left); + stackIsOnLeftSide = true; + } else { + if (stackIsOnLeftSide == true) { + for (int i = 0; i + 1 < stack.size(); ++i) { + result.push_back(indices(stack.at(i))); + result.push_back(indices(right)); + result.push_back(indices(stack.at(i + 1))); + } + stack.first() = stack.last(); + stack.resize(1); + } else { + while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(right, stack.last(), stack.at(stack.size() - 2)))) { + result.push_back(indices(stack.last())); + result.push_back(indices(right)); + result.push_back(indices(stack.at(stack.size() - 2))); + stack.pop_back(); + } + } + stack.add(right); + right = next(right); + stackIsOnLeftSide = false; + } + } + + m_first += m_length + 1; + } + m_parent->m_indices = result; +} + +//============================================================================// +// qTriangulate // +//============================================================================// + +static bool hasElementIndexUint() +{ +#ifndef QT_NO_OPENGL + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (!context) + return false; + return static_cast(context->functions())->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint); +#else + return false; +#endif +} + +Q_GUI_EXPORT QTriangleSet qTriangulate(const qreal *polygon, + int count, uint hint, const QTransform &matrix) +{ + QTriangleSet triangleSet; + if (hasElementIndexUint()) { + QTriangulator triangulator; + triangulator.initialize(polygon, count, hint, matrix); + QVertexSet vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUint(vertexSet.indices); + + } else { + QTriangulator triangulator; + triangulator.initialize(polygon, count, hint, matrix); + QVertexSet vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUshort(vertexSet.indices); + } + return triangleSet; +} + +Q_GUI_EXPORT QTriangleSet qTriangulate(const QVectorPath &path, + const QTransform &matrix, qreal lod) +{ + QTriangleSet triangleSet; + if (hasElementIndexUint()) { + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUint(vertexSet.indices); + } else { + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUshort(vertexSet.indices); + } + return triangleSet; +} + +QTriangleSet qTriangulate(const QPainterPath &path, + const QTransform &matrix, qreal lod) +{ + QTriangleSet triangleSet; + if (hasElementIndexUint()) { + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUint(vertexSet.indices); + } else { + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet vertexSet = triangulator.triangulate(); + triangleSet.vertices = vertexSet.vertices; + triangleSet.indices.setDataUshort(vertexSet.indices); + } + return triangleSet; +} + +QPolylineSet qPolyline(const QVectorPath &path, + const QTransform &matrix, qreal lod) +{ + QPolylineSet polyLineSet; + if (hasElementIndexUint()) { + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet vertexSet = triangulator.polyline(); + polyLineSet.vertices = vertexSet.vertices; + polyLineSet.indices.setDataUint(vertexSet.indices); + } else { + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet vertexSet = triangulator.polyline(); + polyLineSet.vertices = vertexSet.vertices; + polyLineSet.indices.setDataUshort(vertexSet.indices); + } + return polyLineSet; +} + +QPolylineSet qPolyline(const QPainterPath &path, + const QTransform &matrix, qreal lod) +{ + QPolylineSet polyLineSet; + if (hasElementIndexUint()) { + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet vertexSet = triangulator.polyline(); + polyLineSet.vertices = vertexSet.vertices; + polyLineSet.indices.setDataUint(vertexSet.indices); + } else { + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + QVertexSet vertexSet = triangulator.polyline(); + polyLineSet.vertices = vertexSet.vertices; + polyLineSet.indices.setDataUshort(vertexSet.indices); + } + return polyLineSet; +} + +QT_END_NAMESPACE diff --git a/src/gui/painting/qtriangulator_p.h b/src/gui/painting/qtriangulator_p.h new file mode 100644 index 0000000000..4d1aba099c --- /dev/null +++ b/src/gui/painting/qtriangulator_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** 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. +** +** $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 QTRIANGULATOR_P_H +#define QTRIANGULATOR_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 +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QVertexIndexVector +{ +public: + enum Type { + UnsignedInt, + UnsignedShort + }; + + inline Type type() const { return t; } + + inline void setDataUint(const QVector &data) + { + t = UnsignedInt; + indices32 = data; + } + + inline void setDataUshort(const QVector &data) + { + t = UnsignedShort; + indices16 = data; + } + + inline const void* data() const + { + if (t == UnsignedInt) + return indices32.data(); + return indices16.data(); + } + + inline int size() const + { + if (t == UnsignedInt) + return indices32.size(); + return indices16.size(); + } + + inline QVertexIndexVector &operator = (const QVertexIndexVector &other) + { + if (t == UnsignedInt) + indices32 = other.indices32; + else + indices16 = other.indices16; + + t = other.t; + return *this; + } + +private: + + Type t; + QVector indices32; + QVector indices16; +}; + +struct Q_GUI_EXPORT QTriangleSet +{ + inline QTriangleSet() { } + inline QTriangleSet(const QTriangleSet &other) : vertices(other.vertices), indices(other.indices) { } + QTriangleSet &operator = (const QTriangleSet &other) {vertices = other.vertices; indices = other.indices; return *this;} + + // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ... + QVector vertices; // [x[0], y[0], x[1], y[1], x[2], ...] + QVertexIndexVector indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...] +}; + +struct Q_GUI_EXPORT QPolylineSet +{ + inline QPolylineSet() { } + inline QPolylineSet(const QPolylineSet &other) : vertices(other.vertices), indices(other.indices) { } + QPolylineSet &operator = (const QPolylineSet &other) {vertices = other.vertices; indices = other.indices; return *this;} + + QVector vertices; // [x[0], y[0], x[1], y[1], x[2], ...] + QVertexIndexVector indices; // End of polyline is marked with -1. +}; + +// The vertex coordinates of the returned triangle set will be rounded to a grid with a mesh size +// of 1/32. The polygon is first transformed, then scaled by 32, the coordinates are rounded to +// integers, the polygon is triangulated, and then scaled back by 1/32. +// 'hint' should be a combination of QVectorPath::Hints. +// 'lod' is the level of detail. Default is 1. Curves are split into more lines when 'lod' is higher. +QTriangleSet Q_GUI_EXPORT qTriangulate(const qreal *polygon, int count, uint hint = QVectorPath::PolygonHint | QVectorPath::OddEvenFill, const QTransform &matrix = QTransform()); +QTriangleSet Q_GUI_EXPORT qTriangulate(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); +QTriangleSet Q_GUI_EXPORT qTriangulate(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); +QPolylineSet qPolyline(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); +QPolylineSet Q_GUI_EXPORT qPolyline(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); + +QT_END_NAMESPACE + +#endif -- cgit v1.2.3 From 2889ebc9032b06fef812edc2a44d2e1e4e1edcc6 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 15 Aug 2016 15:05:17 +0200 Subject: QSharedPointer: clean up #ifdefs We require Q_COMPILER_RVALUE_REFS and _VARIADIC_TEMPLATES since Qt 5.7, so remove the non-variadic version which anyway has zero test coverage. Also drop #include , as that is included from qglobal.h already, and drop QSKIP from test. Change-Id: I1fc7f7068eac80ad6fd85e1d8f6d33c5c7bb67db Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/tools/qsharedpointer_impl.h | 48 ---------------------- .../tools/qsharedpointer/tst_qsharedpointer.cpp | 6 --- 2 files changed, 54 deletions(-) diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 7ce12bc244..a0c22c9179 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -67,10 +67,6 @@ QT_END_NAMESPACE #endif #include -#if defined(Q_COMPILER_RVALUE_REFS) && defined(Q_COMPILER_VARIADIC_TEMPLATES) -# include // for std::forward -#endif - QT_BEGIN_NAMESPACE @@ -428,7 +424,6 @@ public: QWeakPointer toWeakRef() const; -#if defined(Q_COMPILER_RVALUE_REFS) && defined(Q_COMPILER_VARIADIC_TEMPLATES) template static QSharedPointer create(Args && ...arguments) { @@ -450,49 +445,6 @@ public: result.enableSharedFromThis(result.data()); return result; } -#else - static inline QSharedPointer create() - { - typedef QtSharedPointer::ExternalRefCountWithContiguousData Private; -# ifdef QT_SHAREDPOINTER_TRACK_POINTERS - typename Private::DestroyerFn destroy = &Private::safetyCheckDeleter; -# else - typename Private::DestroyerFn destroy = &Private::deleter; -# endif - QSharedPointer result(Qt::Uninitialized); - result.d = Private::create(&result.value, destroy); - - // now initialize the data - new (result.data()) T(); -# ifdef QT_SHAREDPOINTER_TRACK_POINTERS - internalSafetyCheckAdd(result.d, result.value); -# endif - result.d->setQObjectShared(result.value, true); - result.enableSharedFromThis(result.data()); - return result; - } - - template - static inline QSharedPointer create(const Arg &arg) - { - typedef QtSharedPointer::ExternalRefCountWithContiguousData Private; -# ifdef QT_SHAREDPOINTER_TRACK_POINTERS - typename Private::DestroyerFn destroy = &Private::safetyCheckDeleter; -# else - typename Private::DestroyerFn destroy = &Private::deleter; -# endif - QSharedPointer result(Qt::Uninitialized); - result.d = Private::create(&result.value, destroy); - - // now initialize the data - new (result.data()) T(arg); -# ifdef QT_SHAREDPOINTER_TRACK_POINTERS - internalSafetyCheckAdd(result.d, result.value); -# endif - result.d->setQObjectShared(result.value, true); - return result; - } -#endif private: explicit QSharedPointer(Qt::Initialization) {} diff --git a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp index 1bba41816b..d0a0feb125 100644 --- a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp @@ -1738,9 +1738,6 @@ void tst_QSharedPointer::creating() void tst_QSharedPointer::creatingVariadic() { -#if !defined(Q_COMPILER_RVALUE_REFS) || !defined(Q_COMPILER_VARIADIC_TEMPLATES) - QSKIP("This compiler is not in C++11 mode or it doesn't support rvalue refs and variadic templates"); -#else int i = 42; { @@ -1768,12 +1765,10 @@ void tst_QSharedPointer::creatingVariadic() QCOMPARE(ptr->i, 2); QCOMPARE(ptr->ptr, (void*)0); -#ifdef Q_COMPILER_NULLPTR NoDefaultConstructor2(nullptr, 3); // control check ptr = QSharedPointer::create(nullptr, 3); QCOMPARE(ptr->i, 3); QCOMPARE(ptr->ptr, (void*)nullptr); -#endif } { NoDefaultConstructorRef1 x(i); // control check @@ -1809,7 +1804,6 @@ void tst_QSharedPointer::creatingVariadic() QCOMPARE(ptr->str, QString("bytearray")); QCOMPARE(ptr->i, 42); } -#endif } void tst_QSharedPointer::creatingQObject() -- cgit v1.2.3 From 7b5d14c69953aeaad371d9794e9514a9e480dd46 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 15 Aug 2016 15:09:05 +0200 Subject: QFactoryLoader: clean up #ifdefs We require Q_COMPILER_RVALUE_REFS (implicitly used, but not explicitly checked for) and Q_COMPILER_VARIADIC_TEMPLATES since Qt 5.7, so remove the non-variadic version which anyway has zero test coverage. Change-Id: Ie3658ff6ae71a66df9d35100c3d6df41f2326c80 Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/plugin/qfactoryloader_p.h | 64 ----------------------------------- 1 file changed, 64 deletions(-) diff --git a/src/corelib/plugin/qfactoryloader_p.h b/src/corelib/plugin/qfactoryloader_p.h index 6f62da484b..0876d141c4 100644 --- a/src/corelib/plugin/qfactoryloader_p.h +++ b/src/corelib/plugin/qfactoryloader_p.h @@ -91,8 +91,6 @@ public: QObject *instance(int index) const; }; -#ifdef Q_COMPILER_VARIADIC_TEMPLATES - template PluginInterface *qLoadPlugin(const QFactoryLoader *loader, const QString &key, Args &&...args) { @@ -106,68 +104,6 @@ PluginInterface *qLoadPlugin(const QFactoryLoader *loader, const QString &key, A return nullptr; } -#else - -template - PluginInterface *qLoadPlugin(const QFactoryLoader *loader, const QString &key) -{ - const int index = loader->indexOf(key); - if (index != -1) { - QObject *factoryObject = loader->instance(index); - if (FactoryInterface *factory = qobject_cast(factoryObject)) - if (PluginInterface *result = factory->create(key)) - return result; - } - return 0; -} - -template -PluginInterface *qLoadPlugin(const QFactoryLoader *loader, - const QString &key, - P1 &&p1) -{ - const int index = loader->indexOf(key); - if (index != -1) { - QObject *factoryObject = loader->instance(index); - if (FactoryInterface *factory = qobject_cast(factoryObject)) - if (PluginInterface *result = factory->create(key, std::forward(p1))) - return result; - } - return 0; -} - -template -PluginInterface *qLoadPlugin(const QFactoryLoader *loader, - const QString &key, - P1 &&p1, P2 &&p2) -{ - const int index = loader->indexOf(key); - if (index != -1) { - QObject *factoryObject = loader->instance(index); - if (FactoryInterface *factory = qobject_cast(factoryObject)) - if (PluginInterface *result = factory->create(key, std::forward(p1), std::forward(p2))) - return result; - } - return 0; -} - -template -PluginInterface *qLoadPlugin(const QFactoryLoader *loader, - const QString &key, - P1 &&p1, P2 &&p2, P3 &&p3) -{ - const int index = loader->indexOf(key); - if (index != -1) { - QObject *factoryObject = loader->instance(index); - if (FactoryInterface *factory = qobject_cast(factoryObject)) - if (PluginInterface *result = factory->create(key, std::forward(p1), std::forward(p2), std::forward(p3))) - return result; - } - return 0; -} - -#endif - template Q_DECL_DEPRECATED PluginInterface *qLoadPlugin1(const QFactoryLoader *loader, const QString &key, Arg &&arg) { return qLoadPlugin(loader, key, std::forward(arg)); } -- cgit v1.2.3 From 6e2af81daf927862da93316436c6510fa979fdfa Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 15 Aug 2016 15:11:40 +0200 Subject: QMetaType: clean up #ifdefs We require Q_COMPILER_VARIADIC_TEMPLATES since Qt 5.7, so remove the non-variadic version which anyway has zero test coverage. Change-Id: I24dd22a798e11d797238f9209312802d1d84a672 Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/kernel/qmetatype.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 9abeefa8d8..9e2a5bf75d 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -1622,16 +1622,9 @@ namespace QtPrivate { { return -1; } }; -#ifndef Q_COMPILER_VARIADIC_TEMPLATES // Function pointers don't derive from QObject - template struct IsPointerToTypeDerivedFromQObject { enum { Value = false }; }; - template struct IsPointerToTypeDerivedFromQObject { enum { Value = false }; }; - template struct IsPointerToTypeDerivedFromQObject { enum { Value = false }; }; - template struct IsPointerToTypeDerivedFromQObject { enum { Value = false }; }; -#else template struct IsPointerToTypeDerivedFromQObject { enum { Value = false }; }; -#endif template struct QMetaTypeTypeFlags -- cgit v1.2.3 From 869513a49f7902b5942e439a972807583dab9bf9 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 15 Aug 2016 09:47:11 +0200 Subject: tst_QRegularExpression: clean up - port Q_FOREACH to C++11 range-for (incl. one case of iterating over QHash::keys()) - port uses of inefficient QLists to QVector - add Q_DECLARE_TYPEINFO for types held in Qt containers Fixes errors pointed out by my tree's static checks. Change-Id: I30eb432528fa3008240b5c217d5f2f9ddc3679be Reviewed-by: Giuseppe D'Angelo --- .../qregularexpression/tst_qregularexpression.cpp | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp index 5825bdb6d6..2a93250ba5 100644 --- a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp +++ b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp @@ -61,6 +61,9 @@ struct Match QStringList captured; QHash namedCaptured; }; +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(Match, Q_MOVABLE_TYPE); +QT_END_NAMESPACE Q_DECLARE_METATYPE(Match) @@ -85,9 +88,9 @@ bool operator==(const QRegularExpressionMatch &rem, const Match &m) } } - Q_FOREACH (const QString &name, m.namedCaptured.keys()) { - QString remCaptured = rem.captured(name); - QString mCaptured = m.namedCaptured.value(name); + for (auto it = m.namedCaptured.begin(), end = m.namedCaptured.end(); it != end; ++it) { + const QString remCaptured = rem.captured(it.key()); + const QString mCaptured = it.value(); if (remCaptured != mCaptured || remCaptured.isNull() != mCaptured.isNull() || remCaptured.isEmpty() != mCaptured.isEmpty()) { @@ -115,12 +118,11 @@ bool operator!=(const Match &m, const QRegularExpressionMatch &rem) } -bool operator==(const QRegularExpressionMatchIterator &iterator, const QList &expectedMatchList) +bool operator==(const QRegularExpressionMatchIterator &iterator, const QVector &expectedMatchList) { QRegularExpressionMatchIterator i = iterator; - foreach (const Match &expectedMatch, expectedMatchList) - { + for (const Match &expectedMatch : expectedMatchList) { if (!i.hasNext()) return false; @@ -135,17 +137,17 @@ bool operator==(const QRegularExpressionMatchIterator &iterator, const QList &expectedMatchList, const QRegularExpressionMatchIterator &iterator) +bool operator==(const QVector &expectedMatchList, const QRegularExpressionMatchIterator &iterator) { return operator==(iterator, expectedMatchList); } -bool operator!=(const QRegularExpressionMatchIterator &iterator, const QList &expectedMatchList) +bool operator!=(const QRegularExpressionMatchIterator &iterator, const QVector &expectedMatchList) { return !operator==(iterator, expectedMatchList); } -bool operator!=(const QList &expectedMatchList, const QRegularExpressionMatchIterator &iterator) +bool operator!=(const QVector &expectedMatchList, const QRegularExpressionMatchIterator &iterator) { return !operator==(expectedMatchList, iterator); } @@ -1117,9 +1119,9 @@ void tst_QRegularExpression::globalMatch_data() QTest::addColumn("offset"); QTest::addColumn("matchType"); QTest::addColumn("matchOptions"); - QTest::addColumn >("matchList"); + QTest::addColumn >("matchList"); - QList matchList; + QVector matchList; Match m; matchList.clear(); @@ -1375,7 +1377,7 @@ void tst_QRegularExpression::globalMatch() QFETCH(int, offset); QFETCH(QRegularExpression::MatchType, matchType); QFETCH(QRegularExpression::MatchOptions, matchOptions); - QFETCH(QList, matchList); + QFETCH(QVector, matchList); testMatch(regexp, static_cast(&QRegularExpression::globalMatch), -- cgit v1.2.3 From ac1e87d9f373ad649d989f254b37d2f29ddf25fe Mon Sep 17 00:00:00 2001 From: Konstantin Tokarev Date: Wed, 3 Aug 2016 15:45:02 +0300 Subject: Added capHeight() to QRawFont and QFontMetrics(F) Cap height is an important metric of font, in particular it is required to make decent implementation of "initial-letter" CSS property in QtWebKit. Note that some fonts lack cap height metadata, so we need to fall back to measuring H letter height. Change-Id: Icf69d92159d070889085e20d31f2e397d796d940 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/gui/text/qfontengine.cpp | 15 ++++++ src/gui/text/qfontengine_ft.cpp | 13 +++++ src/gui/text/qfontengine_ft_p.h | 1 + src/gui/text/qfontengine_p.h | 4 ++ src/gui/text/qfontengine_qpf2.cpp | 5 ++ src/gui/text/qfontengine_qpf2_p.h | 1 + src/gui/text/qfontmetrics.cpp | 36 +++++++++++++ src/gui/text/qfontmetrics.h | 2 + src/gui/text/qrawfont.cpp | 17 ++++++ src/gui/text/qrawfont.h | 1 + .../fontdatabases/mac/qfontengine_coretext.mm | 13 +++++ .../fontdatabases/mac/qfontengine_coretext_p.h | 1 + .../platforms/windows/qwindowsfontengine.cpp | 57 +++++++++++++++++++++ src/plugins/platforms/windows/qwindowsfontengine.h | 1 + .../windows/qwindowsfontenginedirectwrite.cpp | 12 +++++ .../windows/qwindowsfontenginedirectwrite.h | 2 + tests/auto/gui/text/qrawfont/testdata.qrc | 1 + tests/auto/gui/text/qrawfont/testfont_os2_v1.ttf | Bin 0 -> 72960 bytes tests/auto/gui/text/qrawfont/tst_qrawfont.cpp | 30 ++++++++++- 19 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 tests/auto/gui/text/qrawfont/testfont_os2_v1.ttf diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index adc8f634dc..dbd47151bd 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -418,6 +418,13 @@ glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix return metrics; } +QFixed QFontEngine::calculatedCapHeight() const +{ + const glyph_t glyph = glyphIndex('H'); + glyph_metrics_t bb = const_cast(this)->boundingBox(glyph); + return bb.height; +} + QFixed QFontEngine::xHeight() const { const glyph_t glyph = glyphIndex('x'); @@ -1703,6 +1710,11 @@ QFixed QFontEngineBox::ascent() const return _size; } +QFixed QFontEngineBox::capHeight() const +{ + return _size; +} + QFixed QFontEngineBox::descent() const { return 0; @@ -2163,6 +2175,9 @@ glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph) QFixed QFontEngineMulti::ascent() const { return engine(0)->ascent(); } +QFixed QFontEngineMulti::capHeight() const +{ return engine(0)->capHeight(); } + QFixed QFontEngineMulti::descent() const { return engine(0)->descent(); } diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 51b1418bc3..b79fa6e9db 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -414,6 +414,7 @@ QFontEngine::Properties QFreetypeFace::properties() const p.italicAngle = 0; p.capHeight = p.ascent; p.lineWidth = face->underline_thickness; + return p; } @@ -1299,6 +1300,18 @@ QFixed QFontEngineFT::ascent() const return v; } +QFixed QFontEngineFT::capHeight() const +{ + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->version >= 2) { + lockFace(); + QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale)); + unlockFace(); + return answer; + } + return calculatedCapHeight(); +} + QFixed QFontEngineFT::descent() const { QFixed v = QFixed::fromFixed(-metrics.descender); diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h index 3cdf0cda47..5ca3721c71 100644 --- a/src/gui/text/qfontengine_ft_p.h +++ b/src/gui/text/qfontengine_ft_p.h @@ -209,6 +209,7 @@ private: int synthesized() const Q_DECL_OVERRIDE; QFixed ascent() const Q_DECL_OVERRIDE; + QFixed capHeight() const Q_DECL_OVERRIDE; QFixed descent() const Q_DECL_OVERRIDE; QFixed leading() const Q_DECL_OVERRIDE; QFixed xHeight() const Q_DECL_OVERRIDE; diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index f3eeddab41..69331cb0bb 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -211,6 +211,7 @@ public: glyph_metrics_t tightBoundingBox(const QGlyphLayout &glyphs); virtual QFixed ascent() const = 0; + virtual QFixed capHeight() const = 0; virtual QFixed descent() const = 0; virtual QFixed leading() const = 0; virtual QFixed xHeight() const; @@ -348,6 +349,7 @@ protected: QFixed lastRightBearing(const QGlyphLayout &glyphs, bool round = false); inline void setUserData(const QVariant &userData) { m_userData = userData; } + QFixed calculatedCapHeight() const; private: struct GlyphCacheEntry { @@ -414,6 +416,7 @@ public: virtual QFontEngine *cloneWithSize(qreal pixelSize) const Q_DECL_OVERRIDE; virtual QFixed ascent() const Q_DECL_OVERRIDE; + virtual QFixed capHeight() const Q_DECL_OVERRIDE; virtual QFixed descent() const Q_DECL_OVERRIDE; virtual QFixed leading() const Q_DECL_OVERRIDE; virtual qreal maxCharWidth() const Q_DECL_OVERRIDE; @@ -451,6 +454,7 @@ public: virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0) Q_DECL_OVERRIDE; virtual QFixed ascent() const Q_DECL_OVERRIDE; + virtual QFixed capHeight() const Q_DECL_OVERRIDE; virtual QFixed descent() const Q_DECL_OVERRIDE; virtual QFixed leading() const Q_DECL_OVERRIDE; virtual QFixed xHeight() const Q_DECL_OVERRIDE; diff --git a/src/gui/text/qfontengine_qpf2.cpp b/src/gui/text/qfontengine_qpf2.cpp index 2e4af09550..110d512d39 100644 --- a/src/gui/text/qfontengine_qpf2.cpp +++ b/src/gui/text/qfontengine_qpf2.cpp @@ -459,6 +459,11 @@ QFixed QFontEngineQPF2::ascent() const return QFixed::fromReal(extractHeaderField(fontData, Tag_Ascent).value()); } +QFixed QFontEngineQPF2::capHeight() const +{ + return calculatedCapHeight(); +} + QFixed QFontEngineQPF2::descent() const { return QFixed::fromReal(extractHeaderField(fontData, Tag_Descent).value()); diff --git a/src/gui/text/qfontengine_qpf2_p.h b/src/gui/text/qfontengine_qpf2_p.h index 50aac245c1..e5c38ffbaf 100644 --- a/src/gui/text/qfontengine_qpf2_p.h +++ b/src/gui/text/qfontengine_qpf2_p.h @@ -172,6 +172,7 @@ public: glyph_metrics_t boundingBox(glyph_t glyph) Q_DECL_OVERRIDE; QFixed ascent() const Q_DECL_OVERRIDE; + QFixed capHeight() const Q_DECL_OVERRIDE; QFixed descent() const Q_DECL_OVERRIDE; QFixed leading() const Q_DECL_OVERRIDE; qreal maxCharWidth() const Q_DECL_OVERRIDE; diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index 2189923b25..aca59d0288 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -274,6 +274,24 @@ int QFontMetrics::ascent() const return qRound(engine->ascent()); } +/*! + Returns the cap height of the font. + + \since 5.8 + + The cap height of a font is the height of a capital letter above + the baseline. It specifically is the height of capital letters + that are flat - such as H or I - as opposed to round letters such + as O, or pointed letters like A, both of which may display overshoot. + + \sa ascent() +*/ +int QFontMetrics::capHeight() const +{ + QFontEngine *engine = d->engineForScript(QChar::Script_Common); + Q_ASSERT(engine != 0); + return qRound(engine->capHeight()); +} /*! Returns the descent of the font. @@ -1138,6 +1156,24 @@ qreal QFontMetricsF::ascent() const return engine->ascent().toReal(); } +/*! + Returns the cap height of the font. + + \since 5.8 + + The cap height of a font is the height of a capital letter above + the baseline. It specifically is the height of capital letters + that are flat - such as H or I - as opposed to round letters such + as O, or pointed letters like A, both of which may display overshoot. + + \sa ascent() +*/ +qreal QFontMetricsF::capHeight() const +{ + QFontEngine *engine = d->engineForScript(QChar::Script_Common); + Q_ASSERT(engine != 0); + return engine->capHeight().toReal(); +} /*! Returns the descent of the font. diff --git a/src/gui/text/qfontmetrics.h b/src/gui/text/qfontmetrics.h index 22e51f29f7..3eac309092 100644 --- a/src/gui/text/qfontmetrics.h +++ b/src/gui/text/qfontmetrics.h @@ -73,6 +73,7 @@ public: { qSwap(d, other.d); } int ascent() const; + int capHeight() const; int descent() const; int height() const; int leading() const; @@ -146,6 +147,7 @@ public: void swap(QFontMetricsF &other) { qSwap(d, other.d); } qreal ascent() const; + qreal capHeight() const; qreal descent() const; qreal height() const; qreal leading() const; diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp index 19ac4f1dbc..1fbe0ce9d1 100644 --- a/src/gui/text/qrawfont.cpp +++ b/src/gui/text/qrawfont.cpp @@ -324,6 +324,23 @@ qreal QRawFont::ascent() const return d->isValid() ? d->fontEngine->ascent().toReal() : 0.0; } +/*! + Returns the cap height of this QRawFont in pixel units. + + \since 5.8 + + The cap height of a font is the height of a capital letter above + the baseline. It specifically is the height of capital letters + that are flat - such as H or I - as opposed to round letters such + as O, or pointed letters like A, both of which may display overshoot. + + \sa QFontMetricsF::capHeight() +*/ +qreal QRawFont::capHeight() const +{ + return d->isValid() ? d->fontEngine->capHeight().toReal() : 0.0; +} + /*! Returns the descent of this QRawFont in pixel units. diff --git a/src/gui/text/qrawfont.h b/src/gui/text/qrawfont.h index 5791c6af16..0252e62370 100644 --- a/src/gui/text/qrawfont.h +++ b/src/gui/text/qrawfont.h @@ -118,6 +118,7 @@ public: QFont::HintingPreference hintingPreference() const; qreal ascent() const; + qreal capHeight() const; qreal descent() const; qreal leading() const; qreal xHeight() const; diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm index 339212db25..646212124b 100644 --- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm @@ -378,6 +378,19 @@ QFixed QCoreTextFontEngine::ascent() const ? QFixed::fromReal(CTFontGetAscent(ctfont)).round() : QFixed::fromReal(CTFontGetAscent(ctfont)); } + +QFixed QCoreTextFontEngine::capHeight() const +{ + QFixed c = QFixed::fromReal(CTFontGetCapHeight(ctfont)); + if (c <= 0) + return calculatedCapHeight(); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + c = c.round(); + + return c; +} + QFixed QCoreTextFontEngine::descent() const { QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont)); diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h b/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h index b7c9edc528..d9ffbb5697 100644 --- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h +++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h @@ -78,6 +78,7 @@ public: glyph_metrics_t boundingBox(glyph_t glyph) Q_DECL_OVERRIDE; QFixed ascent() const Q_DECL_OVERRIDE; + QFixed capHeight() const Q_DECL_OVERRIDE; QFixed descent() const Q_DECL_OVERRIDE; QFixed leading() const Q_DECL_OVERRIDE; QFixed xHeight() const Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 744d882bb2..12340182af 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -192,6 +192,7 @@ void QWindowsFontEngine::getCMap() lineWidth = otm->otmsUnderscoreSize; fsType = otm->otmfsType; free(otm); + } else { unitsPerEm = tm.tmHeight; } @@ -540,6 +541,62 @@ QFixed QWindowsFontEngine::leading() const return tm.tmExternalLeading; } +namespace { +# pragma pack(1) + + 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() +} + +QFixed QWindowsFontEngine::capHeight() const +{ + const QByteArray tableData = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); + if (tableData.size() >= sizeof(OS2Table)) { + const OS2Table *table = reinterpret_cast(tableData.constData()); + if (qFromBigEndian(table->version) >= 2) { + qint16 capHeight = qFromBigEndian(table->capHeight); + if (capHeight > 0) + return QFixed(capHeight) / designToDevice; + } + } + return calculatedCapHeight(); +} QFixed QWindowsFontEngine::xHeight() const { diff --git a/src/plugins/platforms/windows/qwindowsfontengine.h b/src/plugins/platforms/windows/qwindowsfontengine.h index 921351a4b0..b63d8fd282 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.h +++ b/src/plugins/platforms/windows/qwindowsfontengine.h @@ -103,6 +103,7 @@ public: QFixed descent() const Q_DECL_OVERRIDE; QFixed leading() const Q_DECL_OVERRIDE; QFixed xHeight() const Q_DECL_OVERRIDE; + QFixed capHeight() const Q_DECL_OVERRIDE; QFixed averageCharWidth() const Q_DECL_OVERRIDE; qreal maxCharWidth() const Q_DECL_OVERRIDE; qreal minLeftBearing() const Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp index 5408ff41e5..18be3b0ce6 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp @@ -208,6 +208,7 @@ QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *di , m_lineThickness(-1) , m_unitsPerEm(-1) , m_ascent(-1) + , m_capHeight(-1) , m_descent(-1) , m_xHeight(-1) , m_lineGap(-1) @@ -244,6 +245,7 @@ void QWindowsFontEngineDirectWrite::collectMetrics() m_lineThickness = DESIGN_TO_LOGICAL(metrics.underlineThickness); m_ascent = DESIGN_TO_LOGICAL(metrics.ascent); + m_capHeight = DESIGN_TO_LOGICAL(metrics.capHeight); m_descent = DESIGN_TO_LOGICAL(metrics.descent); m_xHeight = DESIGN_TO_LOGICAL(metrics.xHeight); m_lineGap = DESIGN_TO_LOGICAL(metrics.lineGap); @@ -461,6 +463,16 @@ QFixed QWindowsFontEngineDirectWrite::ascent() const : m_ascent; } +QFixed QWindowsFontEngineDirectWrite::capHeight() const +{ + if (m_capHeight <= 0) + return calculatedCapHeight(); + + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_capHeight.round() + : m_capHeight; +} + QFixed QWindowsFontEngineDirectWrite::descent() const { return fontDef.styleStrategy & QFont::ForceIntegerMetrics diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h index f038dcfde4..fb2df00b7e 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h @@ -87,6 +87,7 @@ public: const QTransform &matrix, GlyphFormat) Q_DECL_OVERRIDE; QFixed ascent() const Q_DECL_OVERRIDE; + QFixed capHeight() const Q_DECL_OVERRIDE; QFixed descent() const Q_DECL_OVERRIDE; QFixed leading() const Q_DECL_OVERRIDE; QFixed xHeight() const Q_DECL_OVERRIDE; @@ -122,6 +123,7 @@ private: QFixed m_underlinePosition; int m_unitsPerEm; QFixed m_ascent; + QFixed m_capHeight; QFixed m_descent; QFixed m_xHeight; QFixed m_lineGap; diff --git a/tests/auto/gui/text/qrawfont/testdata.qrc b/tests/auto/gui/text/qrawfont/testdata.qrc index 8f8e32ed24..c7ac9641d1 100644 --- a/tests/auto/gui/text/qrawfont/testdata.qrc +++ b/tests/auto/gui/text/qrawfont/testdata.qrc @@ -1,6 +1,7 @@ testfont_bold_italic.ttf + testfont_os2_v1.ttf ../../../shared/resources/testfont.ttf diff --git a/tests/auto/gui/text/qrawfont/testfont_os2_v1.ttf b/tests/auto/gui/text/qrawfont/testfont_os2_v1.ttf new file mode 100644 index 0000000000..ee8b67d892 Binary files /dev/null and b/tests/auto/gui/text/qrawfont/testfont_os2_v1.ttf differ diff --git a/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp index 471b32dd50..3cf108ed62 100644 --- a/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp +++ b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp @@ -93,6 +93,7 @@ private slots: private: QString testFont; QString testFontBoldItalic; + QString testFontOs2V1; #endif // QT_NO_RAWFONT }; @@ -110,6 +111,7 @@ void tst_QRawFont::initTestCase() { testFont = QFINDTESTDATA("testfont.ttf"); testFontBoldItalic = QFINDTESTDATA("testfont_bold_italic.ttf"); + testFontOs2V1 = QFINDTESTDATA("testfont_os2_v1.ttf"); if (testFont.isEmpty() || testFontBoldItalic.isEmpty()) QFAIL("qrawfont unittest font files not found!"); @@ -184,6 +186,7 @@ void tst_QRawFont::correctFontData_data() QTest::addColumn("hintingPreference"); QTest::addColumn("unitsPerEm"); QTest::addColumn("pixelSize"); + QTest::addColumn("capHeight"); int hintingPreferences[] = { int(QFont::PreferDefaultHinting), @@ -207,7 +210,8 @@ void tst_QRawFont::correctFontData_data() << QFont::Normal << QFont::HintingPreference(*hintingPreference) << qreal(1000.0) - << qreal(10.0); + << qreal(10.0) + << 7; fileName = testFontBoldItalic; title = fileName @@ -221,7 +225,23 @@ void tst_QRawFont::correctFontData_data() << QFont::Bold << QFont::HintingPreference(*hintingPreference) << qreal(1000.0) - << qreal(10.0); + << qreal(10.0) + << 7; + + fileName = testFontOs2V1; + title = fileName + + QLatin1String(": hintingPreference=") + + QString::number(*hintingPreference); + + QTest::newRow(qPrintable(title)) + << fileName + << QString::fromLatin1("QtBidiTestFont") + << QFont::StyleNormal + << QFont::Normal + << QFont::HintingPreference(*hintingPreference) + << qreal(1000.0) + << qreal(10.0) + << 7; ++hintingPreference; } @@ -236,6 +256,7 @@ void tst_QRawFont::correctFontData() QFETCH(QFont::HintingPreference, hintingPreference); QFETCH(qreal, unitsPerEm); QFETCH(qreal, pixelSize); + QFETCH(int, capHeight); QRawFont font(fileName, 10, hintingPreference); QVERIFY(font.isValid()); @@ -246,6 +267,11 @@ void tst_QRawFont::correctFontData() QCOMPARE(font.hintingPreference(), hintingPreference); QCOMPARE(font.unitsPerEm(), unitsPerEm); QCOMPARE(font.pixelSize(), pixelSize); + + // Some platforms return the actual fractional height of the + // H character when the value is missing from the OS/2 table, + // so we ceil it off to match (any touched pixel counts). + QCOMPARE(qCeil(font.capHeight()), capHeight); } void tst_QRawFont::glyphIndices() -- cgit v1.2.3 From aeea30ebba4520b46e1e2780750748d06e4d2101 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 22 Jul 2015 12:04:51 +0200 Subject: qmake: replace a uint:1 with a bool It doesn't save any memory, generates worse code, and prevents a member-swap from being added. Change-Id: Iddc0f1338478e465f34076857e266f1912fbaba6 Reviewed-by: Oswald Buddenhagen --- qmake/generators/makefiledeps.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qmake/generators/makefiledeps.h b/qmake/generators/makefiledeps.h index 3b0c6ce803..034197fd31 100644 --- a/qmake/generators/makefiledeps.h +++ b/qmake/generators/makefiledeps.h @@ -41,10 +41,10 @@ struct SourceDependChildren; class SourceFiles; class QMakeLocalFileName { - uint is_null : 1; + bool is_null; mutable QString real_name, local_name; public: - QMakeLocalFileName() : is_null(1) { } + QMakeLocalFileName() : is_null(true) {} QMakeLocalFileName(const QString &); bool isNull() const { return is_null; } inline const QString &real() const { return real_name; } -- cgit v1.2.3 From fae988e449b790a2a3f6196539e72e92fd227605 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 22 Jul 2015 12:09:58 +0200 Subject: qmake: remove pointless ~VCFilter() It's empty, but non-trivial, thus preventing move special member functions from being generated by the compiler (now that qmake is compiled as C++11, too). Change-Id: I9431311d24da802f147ce10e475936838bb85d41 Reviewed-by: Oswald Buddenhagen --- qmake/generators/win32/msvc_objectmodel.h | 1 - 1 file changed, 1 deletion(-) diff --git a/qmake/generators/win32/msvc_objectmodel.h b/qmake/generators/win32/msvc_objectmodel.h index dfbfaacfa8..933c41469f 100644 --- a/qmake/generators/win32/msvc_objectmodel.h +++ b/qmake/generators/win32/msvc_objectmodel.h @@ -936,7 +936,6 @@ class VCFilter public: // Functions VCFilter(); - ~VCFilter(){} void addFile(const QString& filename); void addFile(const VCFilterFile& fileInfo); -- cgit v1.2.3 From d153a0534e70d18de249b7c6411822e360080a10 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 22 Jul 2015 12:08:51 +0200 Subject: qmake: remove pointless ~VCConfiguration() It's empty, but non-trivial, thus preventing move special member functions from being generated by the compiler (now that qmake is compiled as C++11, too). Change-Id: I7e4b6d7f604020dd5e6da81f7a046202c8b78e09 Reviewed-by: Oswald Buddenhagen --- qmake/generators/win32/msvc_objectmodel.h | 1 - 1 file changed, 1 deletion(-) diff --git a/qmake/generators/win32/msvc_objectmodel.h b/qmake/generators/win32/msvc_objectmodel.h index 933c41469f..c62a8788f3 100644 --- a/qmake/generators/win32/msvc_objectmodel.h +++ b/qmake/generators/win32/msvc_objectmodel.h @@ -860,7 +860,6 @@ class VCConfiguration public: // Functions VCConfiguration(); - ~VCConfiguration(){} bool suppressUnknownOptionWarnings; DotNET CompilerVersion; -- cgit v1.2.3 From b23fb2f07fa9b8aac3524507aa6748bf9ccac08b Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 22 Jul 2015 12:03:02 +0200 Subject: qmake: Mark MakefileGenerator::Compiler as Q_MOVABLE_TYPE Change-Id: I6469ffe3027c8519e80b71f5014f7bd9474e836b Reviewed-by: Oswald Buddenhagen --- qmake/generators/makefile.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qmake/generators/makefile.h b/qmake/generators/makefile.h index a67a0ec0fc..86fec748eb 100644 --- a/qmake/generators/makefile.h +++ b/qmake/generators/makefile.h @@ -155,6 +155,8 @@ protected: }; uint flags, type; }; + friend class QTypeInfo; + void initCompiler(const Compiler &comp); enum VPATHFlag { VPATH_NoFlag = 0x00, @@ -264,6 +266,7 @@ public: bool isWindowsShell() const { return Option::dir_sep == QLatin1String("\\"); } QString shellQuote(const QString &str); }; +Q_DECLARE_TYPEINFO(MakefileGenerator::Compiler, Q_MOVABLE_TYPE); Q_DECLARE_OPERATORS_FOR_FLAGS(MakefileGenerator::FileFixifyTypes) inline void MakefileGenerator::setNoIO(bool o) -- cgit v1.2.3 From 5def42f7832b599d5359d7d31218f0488673d4b9 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 29 Jul 2016 09:51:57 +0200 Subject: Fix alpha from qfontengine_ft to fit qdrawhelper expectations The short-cuts in qdrawhelper's qt_alphargbblit_argb32 expects the alpha value of the A32 glyphs to be 0xff. Since the alpha value is not otherwise used, set it to that. The reported image format is also changed to RGB32, matching coretext and windows fontengines, and expected by the OpenGL paintengine. Change-Id: I0873156d716566f7f1634249155b6c9983a3d0de Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/gui/text/qfontengine_ft.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index b79fa6e9db..db2f88f79a 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -586,8 +586,7 @@ static void convertRGBToARGB_helper(const uchar *src, uint *dst, int width, int uchar green = src[x + 1]; uchar blue = src[x + 1 + offs]; LcdFilter::filterPixel(red, green, blue); - // alpha = green - *dd++ = (green << 24) | (red << 16) | (green << 8) | blue; + *dd++ = (0xFF << 24) | (red << 16) | (green << 8) | blue; } dst += width; src += src_pitch; @@ -612,8 +611,7 @@ static void convertRGBToARGB_V_helper(const uchar *src, uint *dst, int width, in uchar green = src[x + src_pitch]; uchar blue = src[x + src_pitch + offs]; LcdFilter::filterPixel(red, green, blue); - // alpha = green - *dst++ = (green << 24) | (red << 16) | (green << 8) | blue; + *dst++ = (0XFF << 24) | (red << 16) | (green << 8) | blue; } src += 3*src_pitch; } @@ -1855,7 +1853,7 @@ static inline QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEng bytesPerLine = (glyph->width + 3) & ~3; break; case QFontEngine::Format_A32: - format = QImage::Format_ARGB32; + format = QImage::Format_RGB32; bytesPerLine = glyph->width * 4; break; default: -- cgit v1.2.3 From 365a65da708226bd5d832c48ce55d45786abc825 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 16 Aug 2016 14:12:58 +0200 Subject: Remove special case for color fonts from Freetype font engine When adding color font support to the Freetype engine, we added a special case to some functions based on the assumption that color fonts do not contain the latin alphabet. The reason for the assumption is that color fonts are currently most popular for emojis and the most popular ones on Linux did not contain the latin alphabet. But there is nothing special about color fonts making them less likely to contain latin characters than other fonts especially made for non-latin writing systems. If we want an additional fallback when the x character is not available, this should be implemented for all font engines and font types. Change-Id: I1c4aa28d49c38ba7a416c1cae50a1d1c09cbda41 Reviewed-by: Simon Hausmann Reviewed-by: jian liang --- src/gui/text/qfontengine_ft.cpp | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index db2f88f79a..ca6d33c7aa 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -1328,33 +1328,25 @@ QFixed QFontEngineFT::leading() const QFixed QFontEngineFT::xHeight() const { - if (!isScalableBitmap()) { - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); - if (os2 && os2->sxHeight) { - lockFace(); - QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize(); - unlockFace(); - return answer; - } - } else { - return QFixed(freetype->face->size->metrics.y_ppem) * scalableBitmapScaleFactor; + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->sxHeight) { + lockFace(); + QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize(); + unlockFace(); + return answer; } + return QFontEngine::xHeight(); } QFixed QFontEngineFT::averageCharWidth() const { - if (!isScalableBitmap()) { - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); - if (os2 && os2->xAvgCharWidth) { - lockFace(); - QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize(); - unlockFace(); - return answer; - } - } else { - const qreal aspectRatio = (qreal)xsize / ysize; - return QFixed::fromReal(fontDef.pixelSize * aspectRatio); + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->xAvgCharWidth) { + lockFace(); + QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize(); + unlockFace(); + return answer; } return QFontEngine::averageCharWidth(); -- cgit v1.2.3 From aa11287a277d1ca3fab9a4b547deb416d8626557 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 22 Jul 2015 12:12:33 +0200 Subject: qmake: mark VCProjectSingleConfig as movable Has a few too many fields for defining a member-swap (which would be required to mark it shared). Change-Id: Iecbeec9e60a9884cb4a984c58cb192918cef799d Reviewed-by: Oswald Buddenhagen --- qmake/generators/win32/msvc_objectmodel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/qmake/generators/win32/msvc_objectmodel.h b/qmake/generators/win32/msvc_objectmodel.h index c62a8788f3..2e3b9f0691 100644 --- a/qmake/generators/win32/msvc_objectmodel.h +++ b/qmake/generators/win32/msvc_objectmodel.h @@ -1007,6 +1007,7 @@ public: const VCFilter &filterByName(const QString &name) const; const VCFilter &filterForExtraCompiler(const QString &compilerName) const; }; +Q_DECLARE_TYPEINFO(VCProjectSingleConfig, Q_MOVABLE_TYPE); // Tree & Flat view of files -------------------------------------------------- class VCFilter; -- cgit v1.2.3 From e7b6cc270002064728dcab89975302364d0b1d9d Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 22 Jul 2015 12:11:43 +0200 Subject: qmake: remove pointless ~VCProjectSingleConfig() It's empty, but non-trivial, thus preventing move special member functions from being generated by the compiler (now that qmake is compiled as C++11, too). The default ctor was also empty - removed, too. Change-Id: I714db524f681f1fc250d21926245757e97351e87 Reviewed-by: Oswald Buddenhagen --- qmake/generators/win32/msvc_objectmodel.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/qmake/generators/win32/msvc_objectmodel.h b/qmake/generators/win32/msvc_objectmodel.h index 2e3b9f0691..bafc7d563a 100644 --- a/qmake/generators/win32/msvc_objectmodel.h +++ b/qmake/generators/win32/msvc_objectmodel.h @@ -974,9 +974,6 @@ public: Resources, Extras }; - // Functions - VCProjectSingleConfig(){} - ~VCProjectSingleConfig(){} // Variables QString Name; -- cgit v1.2.3 From 839bab13557deb7d8f3ae09e2a0112787512b9f8 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 22 Jul 2015 12:10:44 +0200 Subject: qmake: don't hold VCFilter in QList Change-Id: Ib10539187e0c8e4ffa2000dcad75715a7b931701 Reviewed-by: Oswald Buddenhagen --- qmake/generators/win32/msvc_objectmodel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmake/generators/win32/msvc_objectmodel.h b/qmake/generators/win32/msvc_objectmodel.h index bafc7d563a..0f3665eba8 100644 --- a/qmake/generators/win32/msvc_objectmodel.h +++ b/qmake/generators/win32/msvc_objectmodel.h @@ -960,7 +960,7 @@ public: VCCLCompilerTool CompilerTool; }; -typedef QList VCFilterList; +typedef QVector VCFilterList; class VCProjectSingleConfig { public: -- cgit v1.2.3 From 1e4ba2b4e5f4f16ed349452e1e592df461db29a4 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 22 Jul 2015 12:03:38 +0200 Subject: Don't hold MakefileGenerator::Compiler in QList MakefileGenerater::Compiler is larger than a void*, so holding it in QList is horribly inefficient. Fix by using QVector instead. Change-Id: I9ea173271caf9b4995d311c3864c6967da049380 Reviewed-by: Oswald Buddenhagen --- qmake/generators/makefile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp index 0d1831d987..11b1c8329d 100644 --- a/qmake/generators/makefile.cpp +++ b/qmake/generators/makefile.cpp @@ -612,7 +612,7 @@ MakefileGenerator::init() int x; //build up a list of compilers - QList compilers; + QVector compilers; { const char *builtins[] = { "OBJECTS", "SOURCES", "PRECOMPILED_HEADER", 0 }; for(x = 0; builtins[x]; ++x) { -- cgit v1.2.3 From 3d39c66369300569fa596e2d44b3d7887b6a9005 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sun, 7 Aug 2016 21:13:52 +0300 Subject: QColor: remove unused qt_get_rgb_val() Defined out-of-line, not exported, not used anywhere in QtGui, so it can be removed. Change-Id: Ib049bad7d02e5412c063965cd81c8d95e1c1c183 Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/gui/painting/qcolor_p.cpp | 12 ------------ src/gui/painting/qcolor_p.h | 1 - 2 files changed, 13 deletions(-) diff --git a/src/gui/painting/qcolor_p.cpp b/src/gui/painting/qcolor_p.cpp index 84874bb75f..96f9620300 100644 --- a/src/gui/painting/qcolor_p.cpp +++ b/src/gui/painting/qcolor_p.cpp @@ -337,14 +337,6 @@ bool qt_get_named_rgb(const QChar *name, int len, QRgb *rgb) return get_named_rgb(name_no_space, rgb); } - -uint qt_get_rgb_val(const char *name) -{ - QRgb r = 0; - qt_get_named_rgb(name,&r); - return r; -} - QStringList qt_get_colornames() { int i = 0; @@ -362,10 +354,6 @@ bool qt_get_named_rgb(const char *, QRgb*) return false; } -uint qt_get_rgb_val(const char *) -{ - return 0; -} QStringList qt_get_colornames() { return QStringList(); diff --git a/src/gui/painting/qcolor_p.h b/src/gui/painting/qcolor_p.h index a8d52672f0..b5e92e2ea2 100644 --- a/src/gui/painting/qcolor_p.h +++ b/src/gui/painting/qcolor_p.h @@ -57,7 +57,6 @@ QT_BEGIN_NAMESPACE -uint qt_get_rgb_val(const char *name); bool qt_get_named_rgb(const char *, QRgb*); bool qt_get_named_rgb(const QChar *, int len, QRgb*); bool qt_get_hex_rgb(const char *, QRgb *); -- cgit v1.2.3 From af5ccb7f8c91591ef9dec1ccf0ca211962002a94 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sun, 7 Aug 2016 15:32:40 +0300 Subject: tst_QStyleSheetStyle: Extract Method sample_widgets() The only difference between the sample widget sets was: - widgets << new QLabel("TESTING TESTING"); + widgets << new QLabel("TESTING TESTING"); I chose the latter, because it's the more complex example and neither the hoverColors nor focusColors tests suggest the boldness of the text matters. Part of port away from Q_FOREACH. Change-Id: I9a928de4e781b96ad00a8c9515977c35ebfa6c24 Reviewed-by: Olivier Goffart (Woboq GmbH) --- .../qstylesheetstyle/tst_qstylesheetstyle.cpp | 48 +++++++++------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp index 8d84addedb..b95a17bd4e 100644 --- a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp +++ b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp @@ -817,6 +817,24 @@ static bool testForColors(const QImage& image, const QColor& color, bool ensureP return false; } +static const QList sample_widgets() // returning const to avoid detaching when passing to range-for +{ + QList widgets; + widgets << new QPushButton("TESTING TESTING"); + widgets << new QLineEdit("TESTING TESTING"); + widgets << new QLabel("TESTING TESTING"); + QSpinBox *spinbox = new QSpinBox; + spinbox->setMaximum(1000000000); + spinbox->setValue(123456789); + widgets << spinbox; + QComboBox *combobox = new QComboBox; + combobox->setEditable(true); + combobox->addItems(QStringList() << "TESTING TESTING"); + widgets << combobox; + widgets << new QLabel("TESTING TESTING"); + return widgets; +} + void tst_QStyleSheetStyle::focusColors() { // Tests if colors can be changed by altering the focus of the widget. @@ -833,22 +851,9 @@ void tst_QStyleSheetStyle::focusColors() " (for example, QTBUG-33959)." "That doesn't mean that the feature doesn't work in practice."); #endif - QList widgets; - widgets << new QPushButton("TESTING TESTING"); - widgets << new QLineEdit("TESTING TESTING"); - widgets << new QLabel("TESTING TESTING"); - QSpinBox *spinbox = new QSpinBox; - spinbox->setMaximum(1000000000); - spinbox->setValue(123456789); - widgets << spinbox; - QComboBox *combobox = new QComboBox; - combobox->setEditable(true); - combobox->addItems(QStringList() << "TESTING TESTING"); - widgets << combobox; - widgets << new QLabel("TESTING TESTING"); - foreach (QWidget *widget, widgets) { + for (QWidget *widget : sample_widgets()) { QDialog frame; QLayout* layout = new QGridLayout; @@ -891,21 +896,8 @@ void tst_QStyleSheetStyle::hoverColors() #ifdef Q_OS_OSX QSKIP("This test is fragile on Mac, most likely due to QTBUG-33959."); #endif - QList widgets; - widgets << new QPushButton("TESTING TESTING"); - widgets << new QLineEdit("TESTING TESTING"); - widgets << new QLabel("TESTING TESTING"); - QSpinBox *spinbox = new QSpinBox; - spinbox->setMaximum(1000000000); - spinbox->setValue(123456789); - widgets << spinbox; - QComboBox *combobox = new QComboBox; - combobox->setEditable(true); - combobox->addItems(QStringList() << "TESTING TESTING"); - widgets << combobox; - widgets << new QLabel("TESTING TESTING"); - foreach (QWidget *widget, widgets) { + for (QWidget *widget : sample_widgets()) { //without Qt::X11BypassWindowManagerHint the window manager may move the window after we moved the cursor QDialog frame(0, Qt::X11BypassWindowManagerHint); QLayout* layout = new QGridLayout; -- cgit v1.2.3 From e6f5a7d6c03366d773dfa29ce4c119d7ab941ac4 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 5 Aug 2016 10:04:11 +0300 Subject: tst_QString: clean up - add Q_DECLARE_TYPEINFO for types held in Qt containers (incl. QVariant) - port Q_FOREACH to C++11 range-for - port uses of inefficient QLists to QVector Fixes errors pointed out by my tree's static checks. Change-Id: I7176b4b12ed47ed23166857bd127c459ea2a48d5 Reviewed-by: Olivier Goffart (Woboq GmbH) --- tests/auto/corelib/tools/qstring/tst_qstring.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index 36784435b8..2e35c961c6 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -84,11 +84,11 @@ public: template void apply0(QString &s, MemFun mf) const - { Q_FOREACH (QChar ch, this->pinned) (s.*mf)(ch); } + { for (QChar ch : qAsConst(this->pinned)) (s.*mf)(ch); } template void apply1(QString &s, MemFun mf, A1 a1) const - { Q_FOREACH (QChar ch, this->pinned) (s.*mf)(a1, ch); } + { for (QChar ch : qAsConst(this->pinned)) (s.*mf)(a1, ch); } }; template <> @@ -254,6 +254,9 @@ public: }; } // unnamed namespace +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(CharStarContainer, Q_PRIMITIVE_TYPE); +QT_END_NAMESPACE Q_DECLARE_METATYPE(CharStarContainer) @@ -643,7 +646,7 @@ QString verifyZeroTermination(const QString &str) } while (0) \ /**/ -typedef QList IntList; +typedef QVector IntList; tst_QString::tst_QString() { -- cgit v1.2.3 From 253d2e5135c83730eb1872c6da87116789307d72 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 16 Aug 2016 09:45:09 +0200 Subject: QLinkedList: restore move special member functions for iterators They were masked by the pointless user-defined copy special member functions, which we cannot remove because it would change the way the iterators are passed by value into functions, on some platforms. Add a reminder for Qt 6 to fix the issue for good. Change-Id: I039093894db4a4e5e4bbf94fb346fd90311316c0 Reviewed-by: Olivier Goffart (Woboq GmbH) Reviewed-by: Thiago Macieira --- src/corelib/tools/qlinkedlist.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index e3eed9184f..62cb17c053 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -136,8 +136,12 @@ public: Node *i; inline iterator() : i(0) {} inline iterator(Node *n) : i(n) {} - inline iterator(const iterator &o) : i(o.i) {} - inline iterator &operator=(const iterator &o) { i = o.i; return *this; } +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + iterator(const iterator &other) Q_DECL_NOTHROW : i(other.i) {} + iterator &operator=(const iterator &other) Q_DECL_NOTHROW { i = other.i; return *this; } + iterator(iterator &&other) Q_DECL_NOTHROW : i(other.i) {} + iterator &operator=(iterator &&other) Q_DECL_NOTHROW { return *this = other; } +#endif inline T &operator*() const { return i->t; } inline T *operator->() const { return &i->t; } inline bool operator==(const iterator &o) const { return i == o.i; } @@ -169,9 +173,13 @@ public: Node *i; inline const_iterator() : i(0) {} inline const_iterator(Node *n) : i(n) {} - inline const_iterator(const const_iterator &o) : i(o.i){} inline const_iterator(iterator ci) : i(ci.i){} - inline const_iterator &operator=(const const_iterator &o) { i = o.i; return *this; } +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + const_iterator(const const_iterator &other) Q_DECL_NOTHROW : i(other.i) {} + const_iterator &operator=(const const_iterator &other) Q_DECL_NOTHROW { i = other.i; return *this; } + const_iterator(const_iterator &&other) Q_DECL_NOTHROW : i(other.i) {} + const_iterator &operator=(const_iterator &&other) Q_DECL_NOTHROW { return *this = other; } +#endif inline const T &operator*() const { return i->t; } inline const T *operator->() const { return &i->t; } inline bool operator==(const const_iterator &o) const { return i == o.i; } -- cgit v1.2.3 From 60a695ab5e4e30af6107fc2926cfaf899403b645 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 17 Aug 2016 12:58:03 +0200 Subject: Fix qdoc warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qtbase/src/network/socket/qsctpsocket.cpp:506: warning: Undocumented parameter 'datagram' in QSctpSocket::writeDatagram() qtbase/src/network/ssl/qssldiffiehellmanparameters.cpp:129: warning: Undocumented parameter 'encoding' in QSslDiffieHellmanParameters::QSslDiffieHellmanParameters() qtbase/src/gui/opengl/qopengltextureblitter.cpp:540: warning: Undocumented parameter 'opacity' in QOpenGLTextureBlitter::setOpacity() qtbase/src/gui/opengl/qopengltextureblitter.cpp:522: warning: Undocumented parameter 'swizzle' in QOpenGLTextureBlitter::setRedBlueSwizzle() qtbase/src/testlib/qtestcase.qdoc:1124: warning: Cannot find 'createTouchDevice(...)' in '\fn' QTest::createTouchDevice() Change-Id: Ie8799aa5f63a4ea6c9fe309e2a627fbe3a13b685 Reviewed-by: Laszlo Agocs Reviewed-by: Topi Reiniö --- src/gui/opengl/qopengltextureblitter.cpp | 9 +++++---- src/network/socket/qsctpsocket.cpp | 2 +- src/network/ssl/qssldiffiehellmanparameters.cpp | 2 +- src/testlib/qtestcase.qdoc | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp index 2e74afcbc2..858fc0d857 100644 --- a/src/gui/opengl/qopengltextureblitter.cpp +++ b/src/gui/opengl/qopengltextureblitter.cpp @@ -520,8 +520,8 @@ void QOpenGLTextureBlitter::release() } /*! - Enables or disables swizzling for the red and blue color - channels. An BGRA to RGBA conversion (occurring in the shader on + Sets whether swizzling is enabled for the red and blue color channels to + \a swizzle. An BGRA to RGBA conversion (occurring in the shader on the GPU, instead of a slow CPU-side transformation) can be useful when the source texture contains data from a QImage with a format like QImage::Format_ARGB32 which maps to BGRA on little endian @@ -538,10 +538,11 @@ void QOpenGLTextureBlitter::setRedBlueSwizzle(bool swizzle) } /*! - Changes the opacity. The default opacity is 1.0. + Changes the opacity to \a opacity. The default opacity is 1.0. \note the blitter does not alter the blend state. It is up to the caller of blit() to ensure the correct blend settings are active. + */ void QOpenGLTextureBlitter::setOpacity(float opacity) { @@ -613,7 +614,7 @@ void QOpenGLTextureBlitter::blit(GLuint texture, the source dimensions and will in most cases be set to (0, 0, image width, image height). - For unscaled output the size of \a target and \viewport should + For unscaled output the size of \a target and \a viewport should match. \sa blit() diff --git a/src/network/socket/qsctpsocket.cpp b/src/network/socket/qsctpsocket.cpp index f65ffa765d..cb07e80299 100644 --- a/src/network/socket/qsctpsocket.cpp +++ b/src/network/socket/qsctpsocket.cpp @@ -504,7 +504,7 @@ QNetworkDatagram QSctpSocket::readDatagram() } /*! - Writes a datagram to the buffer of the current write channel. + Writes a \a datagram to the buffer of the current write channel. Returns true on success; otherwise returns false. \sa readDatagram(), inDatagramMode(), currentWriteChannel() diff --git a/src/network/ssl/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp index e6a2fe8a2a..e75ffe4da5 100644 --- a/src/network/ssl/qssldiffiehellmanparameters.cpp +++ b/src/network/ssl/qssldiffiehellmanparameters.cpp @@ -128,7 +128,7 @@ QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(const QByteArray &encod /*! Constructs a QSslDiffieHellmanParameters object by - reading from \a device in either PEM or DER form. + reading from \a device in either PEM or DER form as specified by \a encoding. After construction, the isValid() method should be used to check whether the Diffie-Hellman parameters were valid diff --git a/src/testlib/qtestcase.qdoc b/src/testlib/qtestcase.qdoc index 8bf3b30ffb..bee8a0ad93 100644 --- a/src/testlib/qtestcase.qdoc +++ b/src/testlib/qtestcase.qdoc @@ -1122,10 +1122,10 @@ */ /*! - \fn QTest::createTouchDevice() + \fn QTouchDevice *QTest::createTouchDevice(QTouchDevice::DeviceType devType = QTouchDevice::TouchScreen) \since 5.8 - Creates a dummy touch device for simulation of touch events. + Creates a dummy touch device of type \a devType for simulation of touch events. The touch device will be registered with the QPA window system interface, and deleted automatically when the QCoreApplication is deleted. So you -- cgit v1.2.3 From b0dba5b874be3da9139119df78484da877188a24 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 11 Aug 2016 15:05:55 +0300 Subject: QMimeData: use QStringBuilder more Change-Id: I5a6937545561c51add0d48a618b1921cf9201c4b Reviewed-by: Edward Welbourne Reviewed-by: Marc Mutz --- src/corelib/kernel/qmimedata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/kernel/qmimedata.cpp b/src/corelib/kernel/qmimedata.cpp index 84e680dbd9..95153f8fa9 100644 --- a/src/corelib/kernel/qmimedata.cpp +++ b/src/corelib/kernel/qmimedata.cpp @@ -122,7 +122,7 @@ QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QVariant::Ty const QList list = data.toList(); for (int i = 0; i < list.size(); ++i) { if (list.at(i).type() == QVariant::Url) { - text.append(list.at(i).toUrl().toDisplayString() + QLatin1Char('\n')); + text += list.at(i).toUrl().toDisplayString() + QLatin1Char('\n'); ++numUrls; } } -- cgit v1.2.3 From e4bb9395c2022e6d610c92b2596981c32bc26c18 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 16 Aug 2016 10:05:44 +0200 Subject: Standardize some "We mean it" comments Fix the occurrences where the wrong classes are mentioned. Change-Id: Ia291af77f0f454a39cab93e7376a110c19a07771 Reviewed-by: Lars Knoll --- src/corelib/animation/qanimationgroup_p.h | 4 ++-- src/corelib/animation/qparallelanimationgroup_p.h | 4 ++-- src/corelib/animation/qpropertyanimation_p.h | 4 ++-- src/corelib/animation/qsequentialanimationgroup_p.h | 4 ++-- src/corelib/animation/qvariantanimation_p.h | 4 ++-- src/corelib/codecs/qiconvcodec_p.h | 6 +++--- src/corelib/io/qfilesystemwatcher_fsevents_p.h | 6 +++--- src/corelib/io/qfilesystemwatcher_inotify_p.h | 6 +++--- src/corelib/io/qfilesystemwatcher_kqueue_p.h | 6 +++--- src/corelib/io/qfilesystemwatcher_p.h | 6 +++--- src/corelib/io/qfilesystemwatcher_polling_p.h | 6 +++--- src/corelib/io/qfilesystemwatcher_win_p.h | 6 +++--- src/corelib/io/qipaddress_p.h | 6 +++--- src/corelib/kernel/qfunctions_fake_env_p.h | 6 +++--- src/corelib/thread/qmutexpool_p.h | 4 ++-- src/gui/image/qpaintengine_pic_p.h | 7 +++---- src/gui/image/qpnghandler_p.h | 6 +++--- src/gui/image/qppmhandler_p.h | 6 +++--- src/gui/image/qxbmhandler_p.h | 6 +++--- src/gui/image/qxpmhandler_p.h | 6 +++--- src/gui/opengl/qopenglframebufferobject_p.h | 6 +++--- src/gui/opengl/qopengltextureglyphcache_p.h | 6 +++--- src/gui/text/qcssparser_p.h | 6 +++--- src/gui/text/qtextodfwriter_p.h | 6 +++--- src/network/kernel/qnetworkinterface_p.h | 6 +++--- src/network/kernel/qnetworkproxy_p.h | 7 +++---- src/network/socket/qnativesocketengine_p.h | 6 +++--- src/network/socket/qtcpserver_p.h | 6 +++--- src/network/socket/qtcpsocket_p.h | 6 +++--- src/network/ssl/qasn1element_p.h | 6 +++--- src/network/ssl/qsslcertificate_p.h | 6 +++--- src/network/ssl/qsslsocket_openssl_p.h | 6 +++--- src/network/ssl/qsslsocket_openssl_symbols_p.h | 6 +++--- src/network/ssl/qsslsocket_p.h | 6 +++--- src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h | 6 +++--- src/opengl/qglframebufferobject_p.h | 6 +++--- src/opengl/qglpixelbuffer_p.h | 6 +++--- src/platformsupport/eventdispatchers/qeventdispatcher_glib_p.h | 6 +++--- .../eventdispatchers/qwindowsguieventdispatcher_p.h | 6 +++--- src/plugins/bearer/connman/qconnmanengine.h | 6 +++--- src/plugins/bearer/networkmanager/qnetworkmanagerengine.h | 6 +++--- src/widgets/util/qsystemtrayicon_p.h | 6 +++--- src/widgets/widgets/qwidgetanimator_p.h | 6 +++--- 43 files changed, 123 insertions(+), 125 deletions(-) diff --git a/src/corelib/animation/qanimationgroup_p.h b/src/corelib/animation/qanimationgroup_p.h index 240e5100b9..31c2cd08e8 100644 --- a/src/corelib/animation/qanimationgroup_p.h +++ b/src/corelib/animation/qanimationgroup_p.h @@ -44,8 +44,8 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of QIODevice. This header file may change from version to +// 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. diff --git a/src/corelib/animation/qparallelanimationgroup_p.h b/src/corelib/animation/qparallelanimationgroup_p.h index 752a7c5359..069ba8e51a 100644 --- a/src/corelib/animation/qparallelanimationgroup_p.h +++ b/src/corelib/animation/qparallelanimationgroup_p.h @@ -44,8 +44,8 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of QIODevice. This header file may change from version to +// 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. diff --git a/src/corelib/animation/qpropertyanimation_p.h b/src/corelib/animation/qpropertyanimation_p.h index 5e189f4ced..cfb1f247e0 100644 --- a/src/corelib/animation/qpropertyanimation_p.h +++ b/src/corelib/animation/qpropertyanimation_p.h @@ -44,8 +44,8 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of QIODevice. This header file may change from version to +// 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. diff --git a/src/corelib/animation/qsequentialanimationgroup_p.h b/src/corelib/animation/qsequentialanimationgroup_p.h index edcae7db2a..1b07e1330a 100644 --- a/src/corelib/animation/qsequentialanimationgroup_p.h +++ b/src/corelib/animation/qsequentialanimationgroup_p.h @@ -44,8 +44,8 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of QIODevice. This header file may change from version to +// 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. diff --git a/src/corelib/animation/qvariantanimation_p.h b/src/corelib/animation/qvariantanimation_p.h index c75933dff8..37318a5339 100644 --- a/src/corelib/animation/qvariantanimation_p.h +++ b/src/corelib/animation/qvariantanimation_p.h @@ -44,8 +44,8 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of QIODevice. This header file may change from version to +// 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. diff --git a/src/corelib/codecs/qiconvcodec_p.h b/src/corelib/codecs/qiconvcodec_p.h index dd078fdda4..238351bc81 100644 --- a/src/corelib/codecs/qiconvcodec_p.h +++ b/src/corelib/codecs/qiconvcodec_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/corelib/io/qfilesystemwatcher_fsevents_p.h b/src/corelib/io/qfilesystemwatcher_fsevents_p.h index 5281d64d63..dc4e5bf38b 100644 --- a/src/corelib/io/qfilesystemwatcher_fsevents_p.h +++ b/src/corelib/io/qfilesystemwatcher_fsevents_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/corelib/io/qfilesystemwatcher_inotify_p.h b/src/corelib/io/qfilesystemwatcher_inotify_p.h index 57a5abba4f..777d62dc8a 100644 --- a/src/corelib/io/qfilesystemwatcher_inotify_p.h +++ b/src/corelib/io/qfilesystemwatcher_inotify_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/corelib/io/qfilesystemwatcher_kqueue_p.h b/src/corelib/io/qfilesystemwatcher_kqueue_p.h index 27910a0543..9d4b6d1fe6 100644 --- a/src/corelib/io/qfilesystemwatcher_kqueue_p.h +++ b/src/corelib/io/qfilesystemwatcher_kqueue_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/corelib/io/qfilesystemwatcher_p.h b/src/corelib/io/qfilesystemwatcher_p.h index 1ceda492ef..6c64411f92 100644 --- a/src/corelib/io/qfilesystemwatcher_p.h +++ b/src/corelib/io/qfilesystemwatcher_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/corelib/io/qfilesystemwatcher_polling_p.h b/src/corelib/io/qfilesystemwatcher_polling_p.h index 9101f73c16..6dff08ac05 100644 --- a/src/corelib/io/qfilesystemwatcher_polling_p.h +++ b/src/corelib/io/qfilesystemwatcher_polling_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/corelib/io/qfilesystemwatcher_win_p.h b/src/corelib/io/qfilesystemwatcher_win_p.h index 83f3831c5c..e8f5c49dec 100644 --- a/src/corelib/io/qfilesystemwatcher_win_p.h +++ b/src/corelib/io/qfilesystemwatcher_win_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/corelib/io/qipaddress_p.h b/src/corelib/io/qipaddress_p.h index 5a478ae27b..d95cccb3bd 100644 --- a/src/corelib/io/qipaddress_p.h +++ b/src/corelib/io/qipaddress_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience of -// qurl*.cpp This header file may change from version to version without -// notice, or even be removed. +// 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. // diff --git a/src/corelib/kernel/qfunctions_fake_env_p.h b/src/corelib/kernel/qfunctions_fake_env_p.h index 31d8bd2f26..16d18c4d88 100644 --- a/src/corelib/kernel/qfunctions_fake_env_p.h +++ b/src/corelib/kernel/qfunctions_fake_env_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/corelib/thread/qmutexpool_p.h b/src/corelib/thread/qmutexpool_p.h index b6c837e9f9..58d853b0e3 100644 --- a/src/corelib/thread/qmutexpool_p.h +++ b/src/corelib/thread/qmutexpool_p.h @@ -44,8 +44,8 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of QSettings. This header file may change from version to +// 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. diff --git a/src/gui/image/qpaintengine_pic_p.h b/src/gui/image/qpaintengine_pic_p.h index 277004d055..7c690c1498 100644 --- a/src/gui/image/qpaintengine_pic_p.h +++ b/src/gui/image/qpaintengine_pic_p.h @@ -44,13 +44,12 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of QAbstractItemModel*. This header file may change from version -// to version without notice, or even be removed. +// 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 #include diff --git a/src/gui/image/qpnghandler_p.h b/src/gui/image/qpnghandler_p.h index 0232df7c3f..269df25794 100644 --- a/src/gui/image/qpnghandler_p.h +++ b/src/gui/image/qpnghandler_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/gui/image/qppmhandler_p.h b/src/gui/image/qppmhandler_p.h index 3106d74651..8889d9e663 100644 --- a/src/gui/image/qppmhandler_p.h +++ b/src/gui/image/qppmhandler_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/gui/image/qxbmhandler_p.h b/src/gui/image/qxbmhandler_p.h index c59872f4be..561153376d 100644 --- a/src/gui/image/qxbmhandler_p.h +++ b/src/gui/image/qxbmhandler_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/gui/image/qxpmhandler_p.h b/src/gui/image/qxpmhandler_p.h index 51e5d6dc6c..af3e98d3ff 100644 --- a/src/gui/image/qxpmhandler_p.h +++ b/src/gui/image/qxpmhandler_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/gui/opengl/qopenglframebufferobject_p.h b/src/gui/opengl/qopenglframebufferobject_p.h index b6e332fef1..778a38b823 100644 --- a/src/gui/opengl/qopenglframebufferobject_p.h +++ b/src/gui/opengl/qopenglframebufferobject_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/gui/opengl/qopengltextureglyphcache_p.h b/src/gui/opengl/qopengltextureglyphcache_p.h index da6ba36643..6a1550dbed 100644 --- a/src/gui/opengl/qopengltextureglyphcache_p.h +++ b/src/gui/opengl/qopengltextureglyphcache_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index f5cd5395b2..9f79e9934e 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/gui/text/qtextodfwriter_p.h b/src/gui/text/qtextodfwriter_p.h index c1a5652760..d0dd7d2b5c 100644 --- a/src/gui/text/qtextodfwriter_p.h +++ b/src/gui/text/qtextodfwriter_p.h @@ -48,9 +48,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h index 2dfc93cc18..ec25fdf37e 100644 --- a/src/network/kernel/qnetworkinterface_p.h +++ b/src/network/kernel/qnetworkinterface_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/kernel/qnetworkproxy_p.h b/src/network/kernel/qnetworkproxy_p.h index 0b0dcd3a68..733d8436d0 100644 --- a/src/network/kernel/qnetworkproxy_p.h +++ b/src/network/kernel/qnetworkproxy_p.h @@ -40,14 +40,13 @@ #ifndef QNETWORKPROXY_P_H #define QNETWORKPROXY_P_H - // // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 1ca0fa0213..46c7ae5c55 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -45,9 +45,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/socket/qtcpserver_p.h b/src/network/socket/qtcpserver_p.h index b11dd93718..71dc4d985f 100644 --- a/src/network/socket/qtcpserver_p.h +++ b/src/network/socket/qtcpserver_p.h @@ -45,9 +45,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/socket/qtcpsocket_p.h b/src/network/socket/qtcpsocket_p.h index 17c44b343c..ba1a0aa920 100644 --- a/src/network/socket/qtcpsocket_p.h +++ b/src/network/socket/qtcpsocket_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h index f532cec530..2c5019b4f7 100644 --- a/src/network/ssl/qasn1element_p.h +++ b/src/network/ssl/qasn1element_p.h @@ -45,9 +45,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h index 616b5aec6d..0397845f8d 100644 --- a/src/network/ssl/qsslcertificate_p.h +++ b/src/network/ssl/qsslcertificate_p.h @@ -48,9 +48,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index 755a08ef6e..b2adb3e547 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -59,9 +59,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 508fdc2a4b..b35a895d38 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -60,9 +60,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 696827daff..cec61d07c1 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -47,9 +47,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index 465265da27..95401d10c1 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/opengl/qglframebufferobject_p.h b/src/opengl/qglframebufferobject_p.h index 61ae10fa70..d3af1fdbaa 100644 --- a/src/opengl/qglframebufferobject_p.h +++ b/src/opengl/qglframebufferobject_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/opengl/qglpixelbuffer_p.h b/src/opengl/qglpixelbuffer_p.h index c2040589e3..6416e41773 100644 --- a/src/opengl/qglpixelbuffer_p.h +++ b/src/opengl/qglpixelbuffer_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/platformsupport/eventdispatchers/qeventdispatcher_glib_p.h b/src/platformsupport/eventdispatchers/qeventdispatcher_glib_p.h index 7d57b97276..6d148753bf 100644 --- a/src/platformsupport/eventdispatchers/qeventdispatcher_glib_p.h +++ b/src/platformsupport/eventdispatchers/qeventdispatcher_glib_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher_p.h b/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher_p.h index 5682fc72ef..9a23b5c0bd 100644 --- a/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher_p.h +++ b/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/plugins/bearer/connman/qconnmanengine.h b/src/plugins/bearer/connman/qconnmanengine.h index 23c158ac34..c9ff17f801 100644 --- a/src/plugins/bearer/connman/qconnmanengine.h +++ b/src/plugins/bearer/connman/qconnmanengine.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/plugins/bearer/networkmanager/qnetworkmanagerengine.h b/src/plugins/bearer/networkmanager/qnetworkmanagerengine.h index e8c40a881d..bfe2d746ec 100644 --- a/src/plugins/bearer/networkmanager/qnetworkmanagerengine.h +++ b/src/plugins/bearer/networkmanager/qnetworkmanagerengine.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/widgets/util/qsystemtrayicon_p.h b/src/widgets/util/qsystemtrayicon_p.h index 7aca6631cc..79e824f4b7 100644 --- a/src/widgets/util/qsystemtrayicon_p.h +++ b/src/widgets/util/qsystemtrayicon_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/widgets/widgets/qwidgetanimator_p.h b/src/widgets/widgets/qwidgetanimator_p.h index 6db8b67820..90be22c271 100644 --- a/src/widgets/widgets/qwidgetanimator_p.h +++ b/src/widgets/widgets/qwidgetanimator_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // -- cgit v1.2.3 From b961fca5f0699c4878163dd7c206983c50f801bc Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 15 Aug 2016 15:45:34 +0200 Subject: xcb: mark some types as movable/primitive These are all held in Qt containers. Change-Id: Ie285887b285bbcc0b70962d9f9c52d22d213d022 Reviewed-by: Laszlo Agocs --- src/plugins/platforms/xcb/qxcbconnection.h | 4 ++++ src/plugins/platforms/xcb/qxcbdrag.h | 2 ++ src/plugins/platforms/xcb/qxcbwindow.cpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 1336b3f5d3..34866e5c89 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -580,6 +580,8 @@ private: }; QHash valuatorInfo; }; + friend class QTypeInfo; + friend class QTypeInfo; bool xi2HandleTabletEvent(const void *event, TabletData *tabletData); void xi2ReportTabletEvent(const void *event, TabletData *tabletData); QVector m_tabletData; @@ -682,6 +684,8 @@ private: friend class QXcbEventReader; }; +Q_DECLARE_TYPEINFO(QXcbConnection::TabletData::ValuatorClassInfo, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QXcbConnection::TabletData, Q_MOVABLE_TYPE); #define DISPLAY_FROM_XCB(object) ((Display *)(object->connection()->xlib_display())) #define CREATE_VISUALINFO_FROM_DEFAULT_VISUALID(object) ((XVisualInfo *)(object->connection()->createVisualInfoForDefaultVisualId())) diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index f65dbda05d..cb6e95cc9a 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -159,6 +159,7 @@ private: QPointer drag; QTime time; }; + friend class QTypeInfo; QVector transactions; int transaction_expiry_timer; @@ -167,6 +168,7 @@ private: int findTransactionByTime(xcb_timestamp_t timestamp); xcb_window_t findRealWindow(const QPoint & pos, xcb_window_t w, int md, bool ignoreNonXdndAwareWindows); }; +Q_DECLARE_TYPEINFO(QXcbDrag::Transaction, Q_MOVABLE_TYPE); #endif // QT_NO_DRAGANDDROP diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 10c8c8a2d8..4084dbc1df 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -119,6 +119,8 @@ enum { QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE); + #undef FocusIn enum QX11EmbedFocusInDetail { -- cgit v1.2.3 From d1a4c4f3c9565629b0a634729fcf8ce1ebf0d958 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 15 Aug 2016 15:42:07 +0200 Subject: xcb: eradicate Q_FOREACH loops ... and mark the library with QT_NO_FOREACH. Change-Id: I4924f1419f470b5fa92bb2f3fdefa745b65957f7 Reviewed-by: Friedemann Kleint Reviewed-by: Laszlo Agocs --- src/plugins/platforms/xcb/qxcbmime.cpp | 2 +- src/plugins/platforms/xcb/xcb_qpa_lib.pro | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/platforms/xcb/qxcbmime.cpp b/src/plugins/platforms/xcb/qxcbmime.cpp index f7244739a5..4803c14c4c 100644 --- a/src/plugins/platforms/xcb/qxcbmime.cpp +++ b/src/plugins/platforms/xcb/qxcbmime.cpp @@ -212,7 +212,7 @@ QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, if (format == QLatin1String("text/uri-list")) { const QStringList urls = str.split(QLatin1Char('\n')); QList list; - foreach (const QString &s, urls) { + for (const QString &s : urls) { const QUrl url(s.trimmed()); if (url.isValid()) list.append(url); diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro index 2013f40dd0..7967aee3ab 100644 --- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro +++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro @@ -1,5 +1,6 @@ TARGET = QtXcbQpa CONFIG += no_module_headers internal_module +DEFINES += QT_NO_FOREACH QT += core-private gui-private platformsupport-private -- cgit v1.2.3 From 469b13916983aff4625657eecbb7d2399cac901d Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 3 Jun 2016 14:32:30 +0200 Subject: Register fonts with their given style name Instead of guessing which styles we have available (always bold/italic) register the ones we actually find. Change-Id: I57380d0417411456a3037f8769440e7b43517e0e Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/plugins/platforms/windows/qwindowsfontdatabase.cpp | 16 +++++++++------- .../platforms/windows/qwindowsfontdatabase_ft.cpp | 12 +++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp index 5e75cbf37e..6f236aa588 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -944,7 +944,7 @@ error: return i18n_name; } -static bool addFontToDatabase(const QString &familyName, uchar charSet, +static bool addFontToDatabase(const QString &familyName, const QString &styleName, uchar charSet, const TEXTMETRIC *textmetric, const FONTSIGNATURE *signature, int type, @@ -1012,16 +1012,16 @@ static bool addFontToDatabase(const QString &familyName, uchar charSet, writingSystems.setSupported(ws); } - QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, + QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, style, stretch, antialias, scalable, size, fixed, writingSystems, 0); // add fonts windows can generate for us: - if (weight <= QFont::DemiBold) + if (weight <= QFont::DemiBold && styleName.isEmpty()) QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, style, stretch, antialias, scalable, size, fixed, writingSystems, 0); - if (style != QFont::StyleItalic) + if (style != QFont::StyleItalic && styleName.isEmpty()) QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, 0); - if (weight <= QFont::DemiBold && style != QFont::StyleItalic) + if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty()) QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, 0); @@ -1036,6 +1036,7 @@ static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *t { const ENUMLOGFONTEX *f = reinterpret_cast(logFont); const QString familyName = QString::fromWCharArray(f->elfLogFont.lfFaceName); + const QString styleName = QString::fromWCharArray(f->elfStyle); const uchar charSet = f->elfLogFont.lfCharSet; const bool registerAlias = bool(lParam); @@ -1045,7 +1046,7 @@ static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *t const FONTSIGNATURE *signature = Q_NULLPTR; if (type & TRUETYPE_FONTTYPE) signature = &reinterpret_cast(textmetric)->ntmFontSig; - addFontToDatabase(familyName, charSet, textmetric, signature, type, registerAlias); + addFontToDatabase(familyName, styleName, charSet, textmetric, signature, type, registerAlias); // keep on enumerating return 1; @@ -1456,6 +1457,7 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, // Memory fonts won't show up in enumeration, so do add them the hard way. for (int j = 0; j < families.count(); ++j) { const QString familyName = families.at(j).name; + const QString styleName = families.at(j).style; familyNames << familyName; HDC hdc = GetDC(0); LOGFONT lf; @@ -1468,7 +1470,7 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, TEXTMETRIC textMetrics; GetTextMetrics(hdc, &textMetrics); - addFontToDatabase(familyName, lf.lfCharSet, &textMetrics, &signatures.at(j), + addFontToDatabase(familyName, styleName, lf.lfCharSet, &textMetrics, &signatures.at(j), TRUETYPE_FONTTYPE, true); SelectObject(hdc, oldobj); diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp index d782519c68..b513d9ad7c 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp @@ -160,6 +160,7 @@ static const FontKey *findFontKey(const QString &name, int *indexIn = Q_NULLPTR) } static bool addFontToDatabase(const QString &faceName, + const QString &styleName, const QString &fullName, uchar charSet, const TEXTMETRIC *textmetric, @@ -247,19 +248,19 @@ static bool addFontToDatabase(const QString &faceName, if (!QDir::isAbsolutePath(value)) value.prepend(QFile::decodeName(qgetenv("windir") + "\\Fonts\\")); - QPlatformFontDatabase::registerFont(faceName, QString(), foundryName, weight, style, stretch, + QPlatformFontDatabase::registerFont(faceName, styleName, foundryName, weight, style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); // add fonts windows can generate for us: - if (weight <= QFont::DemiBold) + if (weight <= QFont::DemiBold && styleName.isEmpty()) QPlatformFontDatabase::registerFont(faceName, QString(), foundryName, QFont::Bold, style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); - if (style != QFont::StyleItalic) + if (style != QFont::StyleItalic && styleName.isEmpty()) QPlatformFontDatabase::registerFont(faceName, QString(), foundryName, weight, QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); - if (weight <= QFont::DemiBold && style != QFont::StyleItalic) + if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty()) QPlatformFontDatabase::registerFont(faceName, QString(), foundryName, QFont::Bold, QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); @@ -274,6 +275,7 @@ static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *t { const ENUMLOGFONTEX *f = reinterpret_cast(logFont); const QString faceName = QString::fromWCharArray(f->elfLogFont.lfFaceName); + const QString styleName = QString::fromWCharArray(f->elfStyle); const QString fullName = QString::fromWCharArray(f->elfFullName); const uchar charSet = f->elfLogFont.lfCharSet; @@ -283,7 +285,7 @@ static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *t const FONTSIGNATURE *signature = Q_NULLPTR; if (type & TRUETYPE_FONTTYPE) signature = &reinterpret_cast(textmetric)->ntmFontSig; - addFontToDatabase(faceName, fullName, charSet, textmetric, signature, type, false); + addFontToDatabase(faceName, styleName, fullName, charSet, textmetric, signature, type, false); // keep on enumerating return 1; -- cgit v1.2.3 From 1075f6c7644e098530b317d3ac271328c7ddb481 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Mon, 15 Aug 2016 17:30:44 +0300 Subject: Use QStringRef to optimize memory allocation Replace substring functions that return QString with corresponding functions that return QStringRef. Change-Id: I3c485f89352a1ee66076fba74fd486da9349c354 Reviewed-by: Edward Welbourne --- src/corelib/io/qfilesystemengine_win.cpp | 2 +- src/gui/text/qcssparser.cpp | 2 +- src/gui/text/qtextdocument_p.cpp | 10 +++++----- src/gui/text/qtextengine.cpp | 4 ++-- src/tools/uic/cpp/cppwriteinitialization.cpp | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp index abf346a717..cdb64d08e1 100644 --- a/src/corelib/io/qfilesystemengine_win.cpp +++ b/src/corelib/io/qfilesystemengine_win.cpp @@ -349,7 +349,7 @@ static QString readSymLink(const QFileSystemEntry &link) if (matchVolName.indexIn(result) == 0) { DWORD len; wchar_t buffer[MAX_PATH]; - QString volumeName = result.mid(0, matchVolName.matchedLength()).prepend(QLatin1String("\\\\?\\")); + const QString volumeName = QLatin1String("\\\\?\\") + result.leftRef(matchVolName.matchedLength()); if (GetVolumePathNamesForVolumeName(reinterpret_cast(volumeName.utf16()), buffer, MAX_PATH, &len) != 0) result.replace(0,matchVolName.matchedLength(), QString::fromWCharArray(buffer)); } diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 72c5d5ff33..3e36a6d1c9 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -2107,7 +2107,7 @@ QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences) hexCount = qMin(hexCount, 6); bool ok = false; - ushort code = output.mid(hexStart, hexCount).toUShort(&ok, 16); + ushort code = output.midRef(hexStart, hexCount).toUShort(&ok, 16); if (ok) { output.replace(hexStart - 1, hexCount + 1, QChar(code)); i = hexStart; diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 3537adba9e..7107c7c26e 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -131,7 +131,7 @@ static bool isValidBlockSeparator(QChar ch) || ch == QTextEndOfFrame; } -static bool noBlockInString(const QString &str) +static bool noBlockInString(const QStringRef &str) { return !str.contains(QChar::ParagraphSeparator) && !str.contains(QTextBeginningOfFrame) @@ -320,7 +320,7 @@ void QTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout) void QTextDocumentPrivate::insert_string(int pos, uint strPos, uint length, int format, QTextUndoCommand::Operation op) { // ##### optimize when only appending to the fragment! - Q_ASSERT(noBlockInString(text.mid(strPos, length))); + Q_ASSERT(noBlockInString(text.midRef(strPos, length))); split(pos); uint x = fragments.insert_single(pos, length); @@ -476,7 +476,7 @@ void QTextDocumentPrivate::insert(int pos, const QString &str, int format) if (str.size() == 0) return; - Q_ASSERT(noBlockInString(str)); + Q_ASSERT(noBlockInString(QStringRef(&str))); int strPos = text.length(); text.append(str); @@ -494,7 +494,7 @@ int QTextDocumentPrivate::remove_string(int pos, uint length, QTextUndoCommand:: Q_ASSERT(blocks.size(b) > length); Q_ASSERT(x && fragments.position(x) == (uint)pos && fragments.size(x) == length); - Q_ASSERT(noBlockInString(text.mid(fragments.fragment(x)->stringPosition, length))); + Q_ASSERT(noBlockInString(text.midRef(fragments.fragment(x)->stringPosition, length))); blocks.setSize(b, blocks.size(b)-length); @@ -629,7 +629,7 @@ void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::O if (key+1 != blocks.position(b)) { // qDebug("remove_string from %d length %d", key, X->size_array[0]); - Q_ASSERT(noBlockInString(text.mid(X->stringPosition, X->size_array[0]))); + Q_ASSERT(noBlockInString(text.midRef(X->stringPosition, X->size_array[0]))); w = remove_string(key, X->size_array[0], op); if (needsInsert) { diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index d11f8c34b1..d9b648b170 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -2711,7 +2711,7 @@ static QString stringMidRetainingBidiCC(const QString &string, suffix += c; } - return prefix + ellidePrefix + string.mid(midStart, midLength) + ellideSuffix + suffix; + return prefix + ellidePrefix + string.midRef(midStart, midLength) + ellideSuffix + suffix; } QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int flags, int from, int count) const @@ -2874,7 +2874,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int if (prevCharJoins(layoutData->string, rightPos)) ellipsisText.append(QChar(0x200d) /* ZWJ */); - return layoutData->string.mid(from, leftPos - from) + ellipsisText + layoutData->string.mid(rightPos, to - rightPos); + return layoutData->string.midRef(from, leftPos - from) + ellipsisText + layoutData->string.midRef(rightPos, to - rightPos); } return layoutData->string.mid(from, to - from); diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp index ade3c5db35..031b34b67e 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.cpp +++ b/src/tools/uic/cpp/cppwriteinitialization.cpp @@ -2023,7 +2023,7 @@ void WriteInitialization::addInitializer(Item *item, const QString &name, int column, const QString &value, const QString &directive, bool translatable) const { if (!value.isEmpty()) - item->addSetter(QLatin1String("->set") + name.at(0).toUpper() + name.mid(1) + + item->addSetter(QLatin1String("->set") + name.at(0).toUpper() + name.midRef(1) + QLatin1Char('(') + (column < 0 ? QString() : QString::number(column) + QLatin1String(", ")) + value + QLatin1String(");"), directive, translatable); } -- cgit v1.2.3 From e231581f1ffce1bb6d9ee0dcba32d0fe8d771e1d Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 16 Aug 2016 13:37:49 +0200 Subject: Remove type-punned unions Type punning even over a union is not legal C++, and also causes the compilers to produce poorly performing code. This has already been fixed for the SSE2 code in bilinear sampling but not for the NEON code. Change-Id: Id5e184051e0bd78db730d83ef0dda56ac3206e5b Reviewed-by: Erik Verbruggen --- src/gui/painting/qdrawhelper.cpp | 63 +++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index f0d0ac0283..fa4470a486 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -2243,45 +2243,48 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c const int16x8_t v_disty_ = vshlq_n_s16(v_disty, 4); int32x4_t v_fdx = vdupq_n_s32(fdx*4); - ptrdiff_t secondLine = reinterpret_cast(s2) - reinterpret_cast(s1); - - union Vect_buffer { int32x4_t vect; quint32 i[4]; }; - Vect_buffer v_fx; - - for (int i = 0; i < 4; i++) { - v_fx.i[i] = fx; - fx += fdx; - } + int32x4_t v_fx = vmovq_n_s32(fx); + fx += fdx; + v_fx = vsetq_lane_s32(fx, v_fx, 1); + fx += fdx; + v_fx = vsetq_lane_s32(fx, v_fx, 2); + fx += fdx; + v_fx = vsetq_lane_s32(fx, v_fx, 3); + fx += fdx; const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff); const int32x4_t v_fx_r = vdupq_n_s32(0x0800); while (b < boundedEnd) { - - Vect_buffer tl, tr, bl, br; - - Vect_buffer v_fx_shifted; - v_fx_shifted.vect = vshrq_n_s32(v_fx.vect, 16); - - int32x4_t v_distx = vshrq_n_s32(vaddq_s32(vandq_s32(v_fx.vect, v_ffff_mask), v_fx_r), 12); - - for (int i = 0; i < 4; i++) { - int x1 = v_fx_shifted.i[i]; - const uint *addr_tl = reinterpret_cast(s1) + x1; - const uint *addr_tr = addr_tl + 1; - tl.i[i] = *addr_tl; - tr.i[i] = *addr_tr; - bl.i[i] = *(addr_tl+secondLine); - br.i[i] = *(addr_tr+secondLine); - } - + uint32x4x2_t v_top, v_bot; + + int32x4_t v_fx_shifted = vshrq_n_s32(v_fx, 16); + + int x1 = vgetq_lane_s32(v_fx_shifted, 0); + v_top = vld2q_lane_u32(s1 + x1, v_top, 0); + v_bot = vld2q_lane_u32(s2 + x1, v_bot, 0); + x1 = vgetq_lane_s32(v_fx_shifted, 1); + v_top = vld2q_lane_u32(s1 + x1, v_top, 1); + v_bot = vld2q_lane_u32(s2 + x1, v_bot, 1); + x1 = vgetq_lane_s32(v_fx_shifted, 2); + v_top = vld2q_lane_u32(s1 + x1, v_top, 2); + v_bot = vld2q_lane_u32(s2 + x1, v_bot, 2); + x1 = vgetq_lane_s32(v_fx_shifted, 3); + v_top = vld2q_lane_u32(s1 + x1, v_top, 3); + v_bot = vld2q_lane_u32(s2 + x1, v_bot, 3); + + int32x4_t v_distx = vshrq_n_s32(vaddq_s32(vandq_s32(v_fx, v_ffff_mask), v_fx_r), 12); v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16)); - interpolate_4_pixels_16_neon(vreinterpretq_s16_s32(tl.vect), vreinterpretq_s16_s32(tr.vect), vreinterpretq_s16_s32(bl.vect), vreinterpretq_s16_s32(br.vect), vreinterpretq_s16_s32(v_distx), v_disty, v_disty_, colorMask, invColorMask, v_256, b); + interpolate_4_pixels_16_neon( + vreinterpretq_s16_u32(v_top.val[0]), vreinterpretq_s16_u32(v_top.val[1]), + vreinterpretq_s16_u32(v_bot.val[0]), vreinterpretq_s16_u32(v_bot.val[1]), + vreinterpretq_s16_s32(v_distx), v_disty, v_disty_, + colorMask, invColorMask, v_256, b); b+=4; - v_fx.vect = vaddq_s32(v_fx.vect, v_fdx); + v_fx = vaddq_s32(v_fx, v_fdx); } - fx = v_fx.i[0]; + fx = vgetq_lane_s32(v_fx, 0); #endif } -- cgit v1.2.3 From 8ff2955f99485f85dd2cc235f4c25a77f52633ac Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 16 Aug 2016 13:41:37 +0200 Subject: Add NEON optimization of rotated bilinear sampling SSE2 has had a vectorized code-path for rotated sampling for a long time, but the NEON version has been missing due to a bug in the original commit. This patch reintroduces the NEON optimization. Change-Id: I69d63b60faee9ebf5d69192acd680f35b9619a23 Reviewed-by: Erik Verbruggen --- src/gui/painting/qdrawhelper.cpp | 81 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index fa4470a486..853855b148 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -2417,6 +2417,87 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c } fx = _mm_cvtsi128_si32(v_fx); fy = _mm_cvtsi128_si32(v_fy); +#elif defined(__ARM_NEON__) + BILINEAR_ROTATE_BOUNDS_PROLOG + + const int16x8_t colorMask = vdupq_n_s16(0x00ff); + const int16x8_t invColorMask = vmvnq_s16(colorMask); + const int16x8_t v_256 = vdupq_n_s16(256); + int32x4_t v_fdx = vdupq_n_s32(fdx * 4); + int32x4_t v_fdy = vdupq_n_s32(fdy * 4); + + const uchar *textureData = data->texture.imageData; + const int bytesPerLine = data->texture.bytesPerLine; + + int32x4_t v_fx = vmovq_n_s32(fx); + int32x4_t v_fy = vmovq_n_s32(fy); + fx += fdx; fy += fdy; + v_fx = vsetq_lane_s32(fx, v_fx, 1); + v_fy = vsetq_lane_s32(fy, v_fy, 1); + fx += fdx; fy += fdy; + v_fx = vsetq_lane_s32(fx, v_fx, 2); + v_fy = vsetq_lane_s32(fy, v_fy, 2); + fx += fdx; fy += fdy; + v_fx = vsetq_lane_s32(fx, v_fx, 3); + v_fy = vsetq_lane_s32(fy, v_fy, 3); + fx += fdx; fy += fdy; + + const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff); + const int32x4_t v_round = vdupq_n_s32(0x0800); + + while (b < boundedEnd) { + uint32x4x2_t v_top, v_bot; + + int32x4_t v_fx_shifted, v_fy_shifted; + v_fx_shifted = vshrq_n_s32(v_fx, 16); + v_fy_shifted = vshrq_n_s32(v_fy, 16); + + int x1 = vgetq_lane_s32(v_fx_shifted, 0); + int y1 = vgetq_lane_s32(v_fy_shifted, 0); + const uchar *sl = textureData + bytesPerLine * y1; + const uint *s1 = reinterpret_cast(sl); + const uint *s2 = reinterpret_cast(sl + bytesPerLine); + v_top = vld2q_lane_u32(s1 + x1, v_top, 0); + v_bot = vld2q_lane_u32(s2 + x1, v_bot, 0); + x1 = vgetq_lane_s32(v_fx_shifted, 1); + y1 = vgetq_lane_s32(v_fy_shifted, 1); + sl = textureData + bytesPerLine * y1; + s1 = reinterpret_cast(sl); + s2 = reinterpret_cast(sl + bytesPerLine); + v_top = vld2q_lane_u32(s1 + x1, v_top, 1); + v_bot = vld2q_lane_u32(s2 + x1, v_bot, 1); + x1 = vgetq_lane_s32(v_fx_shifted, 2); + y1 = vgetq_lane_s32(v_fy_shifted, 2); + sl = textureData + bytesPerLine * y1; + s1 = reinterpret_cast(sl); + s2 = reinterpret_cast(sl + bytesPerLine); + v_top = vld2q_lane_u32(s1 + x1, v_top, 2); + v_bot = vld2q_lane_u32(s2 + x1, v_bot, 2); + x1 = vgetq_lane_s32(v_fx_shifted, 3); + y1 = vgetq_lane_s32(v_fy_shifted, 3); + sl = textureData + bytesPerLine * y1; + s1 = reinterpret_cast(sl); + s2 = reinterpret_cast(sl + bytesPerLine); + v_top = vld2q_lane_u32(s1 + x1, v_top, 3); + v_bot = vld2q_lane_u32(s2 + x1, v_bot, 3); + + int32x4_t v_distx = vshrq_n_s32(vaddq_s32(vandq_s32(v_fx, v_ffff_mask), v_round), 12); + int32x4_t v_disty = vshrq_n_s32(vaddq_s32(vandq_s32(v_fy, v_ffff_mask), v_round), 12); + v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16)); + v_disty = vorrq_s32(v_disty, vshlq_n_s32(v_disty, 16)); + int16x8_t v_disty_ = vshlq_n_s16(vreinterpretq_s16_s32(v_disty), 4); + + interpolate_4_pixels_16_neon( + vreinterpretq_s16_u32(v_top.val[0]), vreinterpretq_s16_u32(v_top.val[1]), + vreinterpretq_s16_u32(v_bot.val[0]), vreinterpretq_s16_u32(v_bot.val[1]), + vreinterpretq_s16_s32(v_distx), vreinterpretq_s16_s32(v_disty), + v_disty_, colorMask, invColorMask, v_256, b); + b += 4; + v_fx = vaddq_s32(v_fx, v_fdx); + v_fy = vaddq_s32(v_fy, v_fdy); + } + fx = vgetq_lane_s32(v_fx, 0); + fy = vgetq_lane_s32(v_fy, 0); #endif } -- cgit v1.2.3 From 25dd9c521fc064a71db63b110b7973225ee20c02 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 18 Aug 2016 12:31:26 +0300 Subject: qdnd.cpp: Extract Method imageMimeFormats imageReadMimeFormats and imageWriteMimeFormats had duplicated source code. While touching the code, do some optimizations: - use QStringBuilder more - replace index based loop with 'range for' Change-Id: I174456bf4e55e030930b9b5a4aaef57c722d8f05 Reviewed-by: Edward Welbourne --- src/gui/kernel/qdnd.cpp | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/src/gui/kernel/qdnd.cpp b/src/gui/kernel/qdnd.cpp index 21f9b46654..a59612474b 100644 --- a/src/gui/kernel/qdnd.cpp +++ b/src/gui/kernel/qdnd.cpp @@ -149,17 +149,12 @@ Qt::DropAction QDragManager::drag(QDrag *o) #if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD)) -static QStringList imageReadMimeFormats() +static QStringList imageMimeFormats(const QList &imageFormats) { QStringList formats; - QList imageFormats = QImageReader::supportedImageFormats(); - const int numImageFormats = imageFormats.size(); - formats.reserve(numImageFormats); - for (int i = 0; i < numImageFormats; ++i) { - QString format = QLatin1String("image/"); - format += QString::fromLatin1(imageFormats.at(i).toLower()); - formats.append(format); - } + formats.reserve(imageFormats.size()); + for (const auto &format : imageFormats) + formats.append(QLatin1String("image/") + QLatin1String(format.toLower())); //put png at the front because it is best int pngIndex = formats.indexOf(QLatin1String("image/png")); @@ -169,25 +164,15 @@ static QStringList imageReadMimeFormats() return formats; } - -static QStringList imageWriteMimeFormats() +static inline QStringList imageReadMimeFormats() { - QStringList formats; - QList imageFormats = QImageWriter::supportedImageFormats(); - const int numImageFormats = imageFormats.size(); - formats.reserve(numImageFormats); - for (int i = 0; i < numImageFormats; ++i) { - QString format = QLatin1String("image/"); - format += QString::fromLatin1(imageFormats.at(i).toLower()); - formats.append(format); - } + return imageMimeFormats(QImageReader::supportedImageFormats()); +} - //put png at the front because it is best - int pngIndex = formats.indexOf(QLatin1String("image/png")); - if (pngIndex != -1 && pngIndex != 0) - formats.move(pngIndex, 0); - return formats; +static inline QStringList imageWriteMimeFormats() +{ + return imageMimeFormats(QImageWriter::supportedImageFormats()); } QInternalMimeData::QInternalMimeData() -- cgit v1.2.3 From 9f888d2fde9c5413e5519e0914e9b13638760985 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 27 Jun 2016 15:43:33 +0200 Subject: Support C++17 fallthrough attribute Replaces our mix of comments for annotating intended absence of break in switches with the C++17 attribute [[fallthrough]], or its earlier a clang extension counterpart. Change-Id: I4b2d0b9b5e4425819c7f1bf01608093c536b6d14 Reviewed-by: Thiago Macieira --- qmake/library/qmakebuiltins.cpp | 2 +- qmake/library/qmakeevaluator.cpp | 6 +++--- src/corelib/codecs/qiconvcodec.cpp | 2 +- src/corelib/codecs/qjiscodec.cpp | 2 +- src/corelib/global/qcompilerdetection.h | 12 +++++++++++ src/corelib/global/qglobal.cpp | 14 +++++++++++++ src/corelib/io/qsettings.cpp | 2 +- src/corelib/io/qsettings_win.cpp | 2 +- src/corelib/json/qjson.cpp | 4 ++-- src/corelib/kernel/qmimedata.cpp | 2 +- src/corelib/kernel/qsystemerror.cpp | 3 ++- src/corelib/kernel/qtimerinfo_unix.cpp | 3 +-- src/corelib/kernel/qvariant.cpp | 6 +++--- src/corelib/tools/qdatetime.cpp | 2 +- src/corelib/tools/qdatetimeparser.cpp | 11 +++++----- src/corelib/tools/qregexp.cpp | 6 +++--- src/corelib/tools/qunicodetools.cpp | 4 ++-- src/corelib/xml/qxmlstream.cpp | 24 +++++++++++----------- src/corelib/xml/qxmlstream_p.h | 14 ++++++------- src/dbus/qdbusmarshaller.cpp | 8 ++++---- src/dbus/qdbusmetatype.cpp | 2 +- src/gui/image/qimage.cpp | 4 ++-- src/gui/image/qimagereader.cpp | 2 +- src/gui/image/qpixmap_blitter.cpp | 4 ++-- src/gui/kernel/qplatformgraphicsbufferhelper.cpp | 4 ++-- src/gui/painting/qcssutil.cpp | 2 +- src/gui/painting/qpathclipper.cpp | 4 ++-- src/gui/painting/qpdf.cpp | 4 ++-- src/gui/painting/qplatformbackingstore.cpp | 4 ++-- src/gui/painting/qtransform.cpp | 12 +++++------ src/gui/painting/qtriangulator.cpp | 6 +++--- src/gui/text/qfontsubset.cpp | 2 +- src/gui/text/qtextcursor.cpp | 8 ++++---- src/gui/text/qtextdocumentfragment.cpp | 2 +- src/gui/text/qtextengine.cpp | 14 ++++++------- src/gui/text/qtexthtmlparser.cpp | 6 +++--- src/network/access/qftp.cpp | 2 +- .../access/qhttpnetworkconnectionchannel.cpp | 3 ++- src/network/access/qhttpprotocolhandler.cpp | 7 ++++--- src/network/access/qnetworkcookie.cpp | 2 +- src/network/kernel/qhostaddress.cpp | 2 +- src/network/socket/qnativesocketengine_win.cpp | 2 +- src/network/socket/qsocks5socketengine.cpp | 4 ++-- src/platformsupport/fbconvenience/qfbvthandler.cpp | 2 +- src/plugins/bearer/qnetworksession_impl.cpp | 4 ++-- .../platforms/android/qandroidplatformtheme.cpp | 2 +- src/plugins/platforms/cocoa/qprintengine_mac.mm | 2 +- .../direct2d/qwindowsdirect2dpaintengine.cpp | 2 +- src/plugins/platforms/windows/qwindowscontext.cpp | 3 ++- src/plugins/platforms/windows/qwindowstheme.cpp | 6 +++--- src/printsupport/kernel/qprintengine_pdf.cpp | 2 +- src/printsupport/kernel/qprintengine_win.cpp | 2 +- src/testlib/qtestmouse.h | 4 ++-- src/tools/moc/moc.cpp | 2 +- src/tools/moc/preprocessor.cpp | 10 ++++----- src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp | 8 ++++---- src/widgets/dialogs/qmessagebox.cpp | 3 ++- src/widgets/graphicsview/qgraphicsitem.cpp | 2 +- src/widgets/itemviews/qheaderview.cpp | 4 ++-- src/widgets/itemviews/qtreewidget.cpp | 4 ++-- src/widgets/kernel/qwhatsthis.cpp | 2 +- src/widgets/kernel/qwidget.cpp | 10 ++++----- src/widgets/styles/qcommonstyle.cpp | 6 ++---- src/widgets/styles/qstylesheetstyle.cpp | 2 +- src/widgets/styles/qwindowsstyle.cpp | 5 +++-- src/widgets/styles/qwindowsxpstyle.cpp | 14 ++++++------- src/widgets/util/qflickgesture.cpp | 10 ++++----- src/widgets/widgets/qabstractbutton.cpp | 2 +- src/widgets/widgets/qabstractslider.cpp | 2 +- src/widgets/widgets/qcombobox.cpp | 4 ++-- src/widgets/widgets/qmenu.cpp | 3 ++- 71 files changed, 191 insertions(+), 162 deletions(-) diff --git a/qmake/library/qmakebuiltins.cpp b/qmake/library/qmakebuiltins.cpp index 628210b55b..616b05e82d 100644 --- a/qmake/library/qmakebuiltins.cpp +++ b/qmake/library/qmakebuiltins.cpp @@ -321,7 +321,7 @@ QMakeEvaluator::quoteValue(const ProString &val) break; case 32: quote = true; - // fallthrough + Q_FALLTHROUGH(); default: ret += c; break; diff --git a/qmake/library/qmakeevaluator.cpp b/qmake/library/qmakeevaluator.cpp index 9d7ed2099b..789d6d77f0 100644 --- a/qmake/library/qmakeevaluator.cpp +++ b/qmake/library/qmakeevaluator.cpp @@ -318,7 +318,7 @@ ProStringList QMakeEvaluator::split_value_list(const QStringRef &vals, const Pro --x; } } - // fallthrough + Q_FALLTHROUGH(); default: hadWord = true; break; @@ -1643,7 +1643,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::expandVariableReferences( tokPtr++; continue; } - // fallthrough + Q_FALLTHROUGH(); default: Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token"); break; @@ -2109,7 +2109,7 @@ QString QMakeEvaluator::formatValue(const ProString &val, bool forceQuote) break; case 32: quote = true; - // fallthrough + Q_FALLTHROUGH(); default: ret += c; break; diff --git a/src/corelib/codecs/qiconvcodec.cpp b/src/corelib/codecs/qiconvcodec.cpp index 96f1ee16ac..845155dce0 100644 --- a/src/corelib/codecs/qiconvcodec.cpp +++ b/src/corelib/codecs/qiconvcodec.cpp @@ -411,7 +411,7 @@ QByteArray QIconvCodec::convertFromUnicode(const QChar *uc, int len, ConverterSt switch (errno) { case EILSEQ: ++invalidCount; - // fall through + Q_FALLTHROUGH(); case EINVAL: { inBytes += sizeof(QChar); diff --git a/src/corelib/codecs/qjiscodec.cpp b/src/corelib/codecs/qjiscodec.cpp index 9c628c6012..a8625db054 100644 --- a/src/corelib/codecs/qjiscodec.cpp +++ b/src/corelib/codecs/qjiscodec.cpp @@ -288,7 +288,7 @@ QString QJisCodec::convertToUnicode(const char* chars, int len, ConverterState * result += QLatin1Char(ch); break; } - /* fall through */ + Q_FALLTHROUGH(); case JISX0201_Latin: u = conv->jisx0201ToUnicode(ch); result += QValidChar(u); diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index 70c9e97c13..085e3063b4 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -1341,6 +1341,18 @@ Q_ASSUME_IMPL(valueOfExpression);\ } while (0) +#if QT_HAS_CPP_ATTRIBUTE(fallthrough) +# define Q_FALLTHROUGH() [[fallthrough]] +#elif defined(__cplusplus) +/* Clang can not parse namespaced attributes in C mode, but defines __has_cpp_attribute */ +# if QT_HAS_CPP_ATTRIBUTE(clang::fallthrough) +# define Q_FALLTHROUGH() [[clang::fallthrough]] +# endif +#endif +#ifndef Q_FALLTHROUGH +# define Q_FALLTHROUGH() (void)0 +#endif + /* Sanitize compiler feature availability diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 03216358aa..170dd757d3 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -2949,6 +2949,20 @@ QString QSysInfo::machineHostName() \sa Q_ASSERT(), Q_ASSUME(), qFatal() */ +/*! + \macro void Q_FALLTHROUGH() + \relates + \since 5.8 + + Can be used in switch statements at the end of case block to tell the compiler + and other developers that that the lack of a break statement is intentional. + + This is useful since a missing break statement is often a bug, and some + compilers can be configured to emit warnings when one is not found. + + \sa Q_UNREACHABLE() +*/ + /*! \macro void Q_CHECK_PTR(void *pointer) \relates diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index 856108e417..11f201344e 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -813,7 +813,7 @@ StNormal: ++i; goto StSkipSpaces; } - // fallthrough + Q_FALLTHROUGH(); default: { int j = i + 1; while (j < to) { diff --git a/src/corelib/io/qsettings_win.cpp b/src/corelib/io/qsettings_win.cpp index 7c6b782acd..3f06ab7043 100644 --- a/src/corelib/io/qsettings_win.cpp +++ b/src/corelib/io/qsettings_win.cpp @@ -742,7 +742,7 @@ void QWinSettingsPrivate::set(const QString &uKey, const QVariant &value) } case QVariant::ByteArray: - // fallthrough intended + Q_FALLTHROUGH(); default: { // If the string does not contain '\0', we can use REG_SZ, the native registry diff --git a/src/corelib/json/qjson.cpp b/src/corelib/json/qjson.cpp index 4b98ef076c..e9a1366af0 100644 --- a/src/corelib/json/qjson.cpp +++ b/src/corelib/json/qjson.cpp @@ -339,7 +339,7 @@ bool Value::isValid(const Base *b) const case QJsonValue::Double: if (latinOrIntValue) break; - // fall through + Q_FALLTHROUGH(); case QJsonValue::String: case QJsonValue::Array: case QJsonValue::Object: @@ -418,7 +418,7 @@ uint Value::valueToStore(const QJsonValue &v, uint offset) if (c != INT_MAX) return c; } - // fall through + Q_FALLTHROUGH(); case QJsonValue::String: case QJsonValue::Array: case QJsonValue::Object: diff --git a/src/corelib/kernel/qmimedata.cpp b/src/corelib/kernel/qmimedata.cpp index 95153f8fa9..a550ca6ca1 100644 --- a/src/corelib/kernel/qmimedata.cpp +++ b/src/corelib/kernel/qmimedata.cpp @@ -167,7 +167,7 @@ QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QVariant::Ty case QVariant::List: { if (format != QLatin1String("text/uri-list")) break; - // fall through + Q_FALLTHROUGH(); } case QVariant::Url: { QByteArray ba = data.toByteArray(); diff --git a/src/corelib/kernel/qsystemerror.cpp b/src/corelib/kernel/qsystemerror.cpp index f38daec4f8..3899f24d3b 100644 --- a/src/corelib/kernel/qsystemerror.cpp +++ b/src/corelib/kernel/qsystemerror.cpp @@ -149,12 +149,13 @@ QString QSystemError::toString() const return windowsErrorString(errorCode); #else //unix: fall through as native and standard library are the same + Q_FALLTHROUGH(); #endif case StandardLibraryError: return standardLibraryErrorString(errorCode); default: qWarning("invalid error scope"); - //fall through + Q_FALLTHROUGH(); case NoError: return QLatin1String("No error"); } diff --git a/src/corelib/kernel/qtimerinfo_unix.cpp b/src/corelib/kernel/qtimerinfo_unix.cpp index 56337bdb45..c3b8c86063 100644 --- a/src/corelib/kernel/qtimerinfo_unix.cpp +++ b/src/corelib/kernel/qtimerinfo_unix.cpp @@ -468,7 +468,6 @@ void QTimerInfoList::registerTimer(int timerId, int interval, Qt::TimerType time // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer if (interval >= 20000) { t->timerType = Qt::VeryCoarseTimer; - // fall through } else { t->timeout = expected; if (interval <= 20) { @@ -479,7 +478,7 @@ void QTimerInfoList::registerTimer(int timerId, int interval, Qt::TimerType time } break; } - // fall through + Q_FALLTHROUGH(); case Qt::VeryCoarseTimer: // the very coarse timer is based on full second precision, // so we keep the interval in seconds (round to closest second) diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index f1d38db96c..d21529d124 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -203,7 +203,7 @@ static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok) case QMetaType::QJsonValue: if (!v_cast(d)->isDouble()) break; - // no break + Q_FALLTHROUGH(); #endif case QVariant::Double: case QVariant::Int: @@ -278,7 +278,7 @@ static qulonglong qConvertToUnsignedNumber(const QVariant::Private *d, bool *ok) case QMetaType::QJsonValue: if (!v_cast(d)->isDouble()) break; - // no break + Q_FALLTHROUGH(); #endif case QVariant::Double: case QVariant::Int: @@ -3119,7 +3119,7 @@ bool QVariant::canConvert(int targetTypeId) const case QVariant::Int: if (currentType == QVariant::KeySequence) return true; - // fall through + Q_FALLTHROUGH(); case QVariant::UInt: case QVariant::LongLong: case QVariant::ULongLong: diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index ee5ee5c362..a2c92835fe 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -2656,7 +2656,7 @@ static void setTimeSpec(QDateTimeData &d, Qt::TimeSpec spec, int offsetSeconds) case Qt::TimeZone: // Use system time zone instead spec = Qt::LocalTime; - // fallthrough + Q_FALLTHROUGH(); case Qt::UTC: case Qt::LocalTime: offsetSeconds = 0; diff --git a/src/corelib/tools/qdatetimeparser.cpp b/src/corelib/tools/qdatetimeparser.cpp index 9c9009d636..ac973b047f 100644 --- a/src/corelib/tools/qdatetimeparser.cpp +++ b/src/corelib/tools/qdatetimeparser.cpp @@ -602,7 +602,7 @@ int QDateTimeParser::sectionMaxSize(Section s, int count) const return 2; #else mcount = 7; - // fall through + Q_FALLTHROUGH(); #endif case MonthSection: #ifdef QT_NO_TEXTDATE @@ -770,7 +770,8 @@ int QDateTimeParser::parseSection(const QDateTime ¤tValue, int sectionInde state = Intermediate; } break; - } // else: fall through + } + Q_FALLTHROUGH(); case DaySection: case YearSection: case YearSection2Digits: @@ -1155,7 +1156,7 @@ end: done = true; break; } - // fallthrough + Q_FALLTHROUGH(); default: { int toMin; int toMax; @@ -1489,7 +1490,7 @@ QDateTimeParser::FieldInfo QDateTimeParser::fieldInfo(int index) const switch (sn.type) { case MSecSection: ret |= Fraction; - // fallthrough + Q_FALLTHROUGH(); case SecondSection: case MinuteSection: case Hour24Section: @@ -1509,7 +1510,7 @@ QDateTimeParser::FieldInfo QDateTimeParser::fieldInfo(int index) const switch (sn.count) { case 2: ret |= FixedWidth; - // fallthrough + Q_FALLTHROUGH(); case 1: ret |= (Numeric|AllowPartial); break; diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index f8f3347786..96ddca56af 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -3011,7 +3011,7 @@ int QRegExpEngine::getEscape() case 'I': if (xmlSchemaExtensions) { yyCharClass->setNegative(!yyCharClass->negative()); - // fall through + Q_FALLTHROUGH(); } else { break; } @@ -3051,7 +3051,7 @@ int QRegExpEngine::getEscape() case 'C': if (xmlSchemaExtensions) { yyCharClass->setNegative(!yyCharClass->negative()); - // fall through + Q_FALLTHROUGH(); } else { break; } @@ -3097,7 +3097,7 @@ int QRegExpEngine::getEscape() case 'P': if (xmlSchemaExtensions) { yyCharClass->setNegative(!yyCharClass->negative()); - // fall through + Q_FALLTHROUGH(); } else { break; } diff --git a/src/corelib/tools/qunicodetools.cpp b/src/corelib/tools/qunicodetools.cpp index fad4267edc..be1d88e260 100644 --- a/src/corelib/tools/qunicodetools.cpp +++ b/src/corelib/tools/qunicodetools.cpp @@ -522,13 +522,13 @@ static void getLineBreaks(const ushort *string, quint32 len, QCharAttributes *at // do not change breaks before and after the expression for (quint32 j = nestart + 1; j < pos; ++j) attributes[j].lineBreak = false; - // fall through + Q_FALLTHROUGH(); case LB::NS::None: nelast = LB::NS::XX; // reset state break; case LB::NS::Start: nestart = i; - // fall through + Q_FALLTHROUGH(); default: nelast = necur; break; diff --git a/src/corelib/xml/qxmlstream.cpp b/src/corelib/xml/qxmlstream.cpp index 83b26d50ab..1ffaeca852 100644 --- a/src/corelib/xml/qxmlstream.cpp +++ b/src/corelib/xml/qxmlstream.cpp @@ -976,11 +976,11 @@ bool QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject) case '\r': if ((c = filterCarriageReturn()) == 0) break; - // fall through + Q_FALLTHROUGH(); case '\n': ++lineNumber; lastLineStart = characterOffset + readBufferPos; - // fall through + Q_FALLTHROUGH(); case '\t': textBuffer += QChar(c); continue; @@ -1158,11 +1158,11 @@ inline int QXmlStreamReaderPrivate::fastScanLiteralContent() case '\r': if (filterCarriageReturn() == 0) return n; - // fall through + Q_FALLTHROUGH(); case '\n': ++lineNumber; lastLineStart = characterOffset + readBufferPos; - // fall through + Q_FALLTHROUGH(); case ' ': case '\t': if (normalizeLiterals) @@ -1179,7 +1179,7 @@ inline int QXmlStreamReaderPrivate::fastScanLiteralContent() putChar(c); return n; } - // fall through + Q_FALLTHROUGH(); default: if (c < 0x20) { putChar(c); @@ -1201,11 +1201,11 @@ inline int QXmlStreamReaderPrivate::fastScanSpace() case '\r': if ((c = filterCarriageReturn()) == 0) return n; - // fall through + Q_FALLTHROUGH(); case '\n': ++lineNumber; lastLineStart = characterOffset + readBufferPos; - // fall through + Q_FALLTHROUGH(); case ' ': case '\t': textBuffer += QChar(c); @@ -1259,11 +1259,11 @@ inline int QXmlStreamReaderPrivate::fastScanContentCharList() case '\r': if ((c = filterCarriageReturn()) == 0) return n; - // fall through + Q_FALLTHROUGH(); case '\n': ++lineNumber; lastLineStart = characterOffset + readBufferPos; - // fall through + Q_FALLTHROUGH(); case ' ': case '\t': textBuffer += QChar(ushort(c)); @@ -1275,7 +1275,7 @@ inline int QXmlStreamReaderPrivate::fastScanContentCharList() putChar(c); return n; } - // fall through + Q_FALLTHROUGH(); default: if (c < 0x20) { putChar(c); @@ -1339,7 +1339,7 @@ inline int QXmlStreamReaderPrivate::fastScanName(int *prefix) putChar(c); return n; } - // fall through + Q_FALLTHROUGH(); default: textBuffer += QChar(c); ++n; @@ -2123,7 +2123,7 @@ QString QXmlStreamReader::readElementText(ReadElementTextBehaviour behaviour) result += readElementText(behaviour); break; } - // Fall through (for ErrorOnUnexpectedElement) + Q_FALLTHROUGH(); default: if (d->error || behaviour == ErrorOnUnexpectedElement) { if (!d->error) diff --git a/src/corelib/xml/qxmlstream_p.h b/src/corelib/xml/qxmlstream_p.h index c68d6f0f53..b62cc9ac39 100644 --- a/src/corelib/xml/qxmlstream_p.h +++ b/src/corelib/xml/qxmlstream_p.h @@ -1048,7 +1048,7 @@ bool QXmlStreamReaderPrivate::parse() dtdName.clear(); dtdPublicId.clear(); dtdSystemId.clear(); - // fall through + Q_FALLTHROUGH(); case QXmlStreamReader::Comment: case QXmlStreamReader::Characters: isCDATA = false; @@ -1080,7 +1080,7 @@ bool QXmlStreamReaderPrivate::parse() return false; } #endif - // fall through + Q_FALLTHROUGH(); default: clearTextBuffer(); ; @@ -1124,7 +1124,7 @@ bool QXmlStreamReaderPrivate::parse() } else { break; } - // fall through + Q_FALLTHROUGH(); case ~0U: { token = EOF_SYMBOL; if (!tagsDone && !inParseEntity) { @@ -1338,7 +1338,7 @@ bool QXmlStreamReaderPrivate::parse() case 17: case 18: dtdName = symString(3); - // fall through + Q_FALLTHROUGH(); case 19: case 20: @@ -1480,7 +1480,7 @@ bool QXmlStreamReaderPrivate::parse() if (entityDeclaration.parameter) raiseWellFormedError(QXmlStream::tr("NDATA in parameter entity declaration.")); } - //fall through + Q_FALLTHROUGH(); case 94: case 95: { @@ -1588,7 +1588,7 @@ bool QXmlStreamReaderPrivate::parse() case 129: isWhitespace = false; - // fall through + Q_FALLTHROUGH(); case 130: sym(1).len += fastScanContentCharList(); @@ -1760,7 +1760,7 @@ bool QXmlStreamReaderPrivate::parse() case 236: isEmptyElement = true; - // fall through + Q_FALLTHROUGH(); case 237: setType(QXmlStreamReader::StartElement); diff --git a/src/dbus/qdbusmarshaller.cpp b/src/dbus/qdbusmarshaller.cpp index 7ba7c79808..4ea6cefff6 100644 --- a/src/dbus/qdbusmarshaller.cpp +++ b/src/dbus/qdbusmarshaller.cpp @@ -318,7 +318,7 @@ void QDBusMarshaller::open(QDBusMarshaller &sub, int code, const char *signature case DBUS_TYPE_ARRAY: *ba += char(code); *ba += signature; - // fall through + Q_FALLTHROUGH(); case DBUS_TYPE_DICT_ENTRY: sub.closeCode = 0; @@ -495,9 +495,9 @@ bool QDBusMarshaller::appendVariantInternal(const QVariant &arg) return true; default: - ; // fall through + ; } - // fall through + Q_FALLTHROUGH(); case DBUS_TYPE_STRUCT: case DBUS_STRUCT_BEGIN_CHAR: @@ -513,7 +513,7 @@ bool QDBusMarshaller::appendVariantInternal(const QVariant &arg) append(qvariant_cast(arg)); return true; } - // fall through + Q_FALLTHROUGH(); default: qWarning("QDBusMarshaller::appendVariantInternal: Found unknown D-BUS type '%s'", diff --git a/src/dbus/qdbusmetatype.cpp b/src/dbus/qdbusmetatype.cpp index 59a71ade3e..fb2b407997 100644 --- a/src/dbus/qdbusmetatype.cpp +++ b/src/dbus/qdbusmetatype.cpp @@ -373,7 +373,7 @@ int QDBusMetaType::signatureToType(const char *signature) return qMetaTypeId >(); } - // fall through + Q_FALLTHROUGH(); default: return QMetaType::UnknownType; } diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index bd32ca7bee..9b14500e7a 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1888,7 +1888,7 @@ void QImage::invertPixels(InvertMode mode) case QImage::Format_RGBA8888: if (mode == InvertRgba) break; - // no break + Q_FALLTHROUGH(); case QImage::Format_RGBX8888: #if Q_BYTE_ORDER == Q_BIG_ENDIAN xorbits = 0xffffff00; @@ -1900,7 +1900,7 @@ void QImage::invertPixels(InvertMode mode) case QImage::Format_ARGB32: if (mode == InvertRgba) break; - // no break + Q_FALLTHROUGH(); case QImage::Format_RGB32: xorbits = 0x00ffffff; break; diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 45d57856aa..0320d032b0 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -1151,7 +1151,7 @@ bool QImageReader::autoTransform() const case QImageReaderPrivate::UsePluginDefault: if (d->initHandler()) return d->handler->supportsOption(QImageIOHandler::TransformedByDefault); - // no break + Q_FALLTHROUGH(); default: break; } diff --git a/src/gui/image/qpixmap_blitter.cpp b/src/gui/image/qpixmap_blitter.cpp index 4032176efc..950695a9d7 100644 --- a/src/gui/image/qpixmap_blitter.cpp +++ b/src/gui/image/qpixmap_blitter.cpp @@ -113,10 +113,10 @@ int QBlittablePlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) con return qRound(h * 25.4 / qt_defaultDpiY()); case QPaintDevice::PdmDepth: return 32; - case QPaintDevice::PdmDpiX: // fall-through + case QPaintDevice::PdmDpiX: case QPaintDevice::PdmPhysicalDpiX: return qt_defaultDpiX(); - case QPaintDevice::PdmDpiY: // fall-through + case QPaintDevice::PdmDpiY: case QPaintDevice::PdmPhysicalDpiY: return qt_defaultDpiY(); case QPaintDevice::PdmDevicePixelRatio: diff --git a/src/gui/kernel/qplatformgraphicsbufferhelper.cpp b/src/gui/kernel/qplatformgraphicsbufferhelper.cpp index 82a88eca04..2afb5e6ba5 100644 --- a/src/gui/kernel/qplatformgraphicsbufferhelper.cpp +++ b/src/gui/kernel/qplatformgraphicsbufferhelper.cpp @@ -154,14 +154,14 @@ bool QPlatformGraphicsBufferHelper::bindSWToTexture(const QPlatformGraphicsBuffe switch (imageformat) { case QImage::Format_ARGB32_Premultiplied: premultiplied = true; - // no break + Q_FALLTHROUGH(); case QImage::Format_RGB32: case QImage::Format_ARGB32: swizzle = true; break; case QImage::Format_RGBA8888_Premultiplied: premultiplied = true; - // no break + Q_FALLTHROUGH(); case QImage::Format_RGBX8888: case QImage::Format_RGBA8888: break; diff --git a/src/gui/painting/qcssutil.cpp b/src/gui/painting/qcssutil.cpp index a826532b43..2d514e14e0 100644 --- a/src/gui/painting/qcssutil.cpp +++ b/src/gui/painting/qcssutil.cpp @@ -197,7 +197,7 @@ void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, q if ((style == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge)) || (style == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge))) c = c.color().lighter(); - // fall through! + Q_FALLTHROUGH(); case BorderStyle_Solid: { p->setPen(Qt::NoPen); p->setBrush(c); diff --git a/src/gui/painting/qpathclipper.cpp b/src/gui/painting/qpathclipper.cpp index 4f2b59c775..f92a681eca 100644 --- a/src/gui/painting/qpathclipper.cpp +++ b/src/gui/painting/qpathclipper.cpp @@ -1760,7 +1760,7 @@ static bool bool_op(bool a, bool b, QPathClipper::Operation op) switch (op) { case QPathClipper::BoolAnd: return a && b; - case QPathClipper::BoolOr: // fall-through + case QPathClipper::BoolOr: case QPathClipper::Simplify: return a || b; case QPathClipper::BoolSub: @@ -1956,7 +1956,7 @@ QPointF intersectLine(const QPointF &a, const QPointF &b, qreal t) { QLineF line(a, b); switch (edge) { - case Left: // fall-through + case Left: case Right: return line.pointAt((t - a.x()) / (b.x() - a.x())); default: diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 34f1c51f6d..d246c96da6 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -2091,7 +2091,7 @@ int QPdfEnginePrivate::generateLinearGradientShader(const QLinearGradient *gradi break; case QGradient::ReflectSpread: reflect = true; - // fall through + Q_FALLTHROUGH(); case QGradient::RepeatSpread: { // calculate required bounds QRectF pageRect = m_pageLayout.fullRectPixels(resolution); @@ -2154,7 +2154,7 @@ int QPdfEnginePrivate::generateRadialGradientShader(const QRadialGradient *gradi break; case QGradient::ReflectSpread: reflect = true; - // fall through + Q_FALLTHROUGH(); case QGradient::RepeatSpread: { Q_ASSERT(qFuzzyIsNull(r0)); // QPainter emulates if this is not 0 diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index e2c5a82ffc..0c5de36981 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -471,14 +471,14 @@ GLuint QPlatformBackingStore::toTexture(const QRegion &dirtyRegion, QSize *textu switch (image.format()) { case QImage::Format_ARGB32_Premultiplied: *flags |= TexturePremultiplied; - // no break + Q_FALLTHROUGH(); case QImage::Format_RGB32: case QImage::Format_ARGB32: *flags |= TextureSwizzle; break; case QImage::Format_RGBA8888_Premultiplied: *flags |= TexturePremultiplied; - // no break + Q_FALLTHROUGH(); case QImage::Format_RGBX8888: case QImage::Format_RGBA8888: break; diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 6058811176..2d841b2953 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -446,7 +446,7 @@ QTransform &QTransform::translate(qreal dx, qreal dy) break; case TxProject: m_33 += dx*m_13 + dy*m_23; - // Fall through + Q_FALLTHROUGH(); case TxShear: case TxRotate: affine._dx += dx*affine._m11 + dy*affine._m21; @@ -508,12 +508,12 @@ QTransform & QTransform::scale(qreal sx, qreal sy) case TxProject: m_13 *= sx; m_23 *= sy; - // fall through + Q_FALLTHROUGH(); case TxRotate: case TxShear: affine._m12 *= sx; affine._m21 *= sy; - // fall through + Q_FALLTHROUGH(); case TxScale: affine._m11 *= sx; affine._m22 *= sy; @@ -581,7 +581,7 @@ QTransform & QTransform::shear(qreal sh, qreal sv) m_13 += tm13; m_23 += tm23; } - // fall through + Q_FALLTHROUGH(); case TxRotate: case TxShear: { qreal tm11 = sv*affine._m21; @@ -663,7 +663,7 @@ QTransform & QTransform::rotate(qreal a, Qt::Axis axis) qreal tm23 = -sina*m_13 + cosa*m_23; m_13 = tm13; m_23 = tm23; - // fall through + Q_FALLTHROUGH(); } case TxRotate: case TxShear: { @@ -742,7 +742,7 @@ QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis) qreal tm23 = -sina*m_13 + cosa*m_23; m_13 = tm13; m_23 = tm23; - // fall through + Q_FALLTHROUGH(); } case TxRotate: case TxShear: { diff --git a/src/gui/painting/qtriangulator.cpp b/src/gui/painting/qtriangulator.cpp index 7906011cd2..6604d407f0 100644 --- a/src/gui/painting/qtriangulator.cpp +++ b/src/gui/painting/qtriangulator.cpp @@ -874,7 +874,7 @@ void QTriangulator::initialize(const QVectorPath &path, const QTransform &mat case QPainterPath::MoveToElement: if (!m_indices.isEmpty()) m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON - // Fall through. + Q_FALLTHROUGH(); case QPainterPath::LineToElement: m_indices.push_back(T(m_vertices.size())); m_vertices.resize(m_vertices.size() + 1); @@ -2100,7 +2100,7 @@ void QTriangulator::SimpleToMonotone::monotoneDecomposition() } else { qWarning("Inconsistent polygon. (#3)"); } - // Fall through. + Q_FALLTHROUGH(); case StartVertex: if (m_clockwiseOrder) { leftEdgeNode = searchEdgeLeftOfEdge(j); @@ -2129,7 +2129,7 @@ void QTriangulator::SimpleToMonotone::monotoneDecomposition() } else { qWarning("Inconsistent polygon. (#4)"); } - // Fall through. + Q_FALLTHROUGH(); case EndVertex: if (m_clockwiseOrder) { if (m_edges.at(m_edges.at(i).helper).type == MergeVertex) diff --git a/src/gui/text/qfontsubset.cpp b/src/gui/text/qfontsubset.cpp index c8f0393084..49638832e0 100644 --- a/src/gui/text/qfontsubset.cpp +++ b/src/gui/text/qfontsubset.cpp @@ -740,7 +740,7 @@ static void convertPath(const QPainterPath &path, QVector *points, QV points->takeLast(); endPoints->append(points->size() - 1); } - // fall through + Q_FALLTHROUGH(); case QPainterPath::LineToElement: p.flags = OnCurve; break; diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp index f824d1b369..f32c31d18e 100644 --- a/src/gui/text/qtextcursor.cpp +++ b/src/gui/text/qtextcursor.cpp @@ -438,7 +438,7 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor if (relativePos < blockIt.length()-1) ++position; - // FALL THROUGH! + Q_FALLTHROUGH(); } case QTextCursor::PreviousWord: case QTextCursor::WordLeft: @@ -590,9 +590,9 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor adjustX = false; break; } - case QTextCursor::NextCell: // fall through - case QTextCursor::PreviousCell: // fall through - case QTextCursor::NextRow: // fall through + case QTextCursor::NextCell: + case QTextCursor::PreviousCell: + case QTextCursor::NextRow: case QTextCursor::PreviousRow: { QTextTable *table = qobject_cast(priv->frameAt(position)); if (!table) diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp index 5727b34e86..079b2e01f7 100644 --- a/src/gui/text/qtextdocumentfragment.cpp +++ b/src/gui/text/qtextdocumentfragment.cpp @@ -827,7 +827,7 @@ bool QTextHtmlImporter::closeTag() case Html_div: if (closedNode->children.isEmpty()) break; - // fall through + Q_FALLTHROUGH(); default: if (closedNode->isBlock()) blockTagClosed = true; diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index d9b648b170..1aaacdd00d 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -508,7 +508,7 @@ static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiCon case QChar::DirAN: if (eor >= 0) appendItems(analysis, sor, eor, control, dir); - // fall through + Q_FALLTHROUGH(); case QChar::DirR: case QChar::DirAL: dir = QChar::DirR; eor = current; status.eor = QChar::DirR; break; @@ -564,7 +564,7 @@ static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiCon status.eor = QChar::DirON; dir = QChar::DirAN; } - // fall through + Q_FALLTHROUGH(); case QChar::DirEN: case QChar::DirL: eor = current; @@ -744,7 +744,7 @@ static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiCon status.last = QChar::DirL; break; } - // fall through + Q_FALLTHROUGH(); default: status.last = dirCurrent; } @@ -1663,7 +1663,7 @@ void QTextEngine::itemize() const analysis->bidiLevel = control.baseLevel(); break; } - // fall through + Q_FALLTHROUGH(); default: analysis->flags = QScriptAnalysis::None; break; @@ -2250,7 +2250,6 @@ void QTextEngine::justify(const QScriptLine &line) case Justification_Prohibited: break; case Justification_Space: - // fall through case Justification_Arabic_Space: if (kashida_pos >= 0) { // qDebug("kashida position at %d in word", kashida_pos); @@ -2263,7 +2262,7 @@ void QTextEngine::justify(const QScriptLine &line) } kashida_pos = -1; kashida_type = Justification_Arabic_Normal; - // fall through + Q_FALLTHROUGH(); case Justification_Character: set(&justificationPoints[nPoints++], justification, g.mid(i), fontEngine(si)); maxJustify = qMax(maxJustify, justification); @@ -2966,9 +2965,8 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const switch (tabSpec.type) { case QTextOption::CenterTab: length /= 2; - // fall through + Q_FALLTHROUGH(); case QTextOption::DelimiterTab: - // fall through case QTextOption::RightTab: tab = QFixed::fromReal(tabSpec.position) * dpiScale - length; if (tab < x) // default to tab taking no space diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index 7bca50325c..7fac3331f1 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1871,7 +1871,7 @@ QVector standardDeclarationForNode(const QTextHtmlParserNode decls << decl; if (node.id == Html_b || node.id == Html_strong) break; - // Delibrate fall through + Q_FALLTHROUGH(); case Html_big: case Html_small: if (node.id != Html_th) { @@ -1892,7 +1892,7 @@ QVector standardDeclarationForNode(const QTextHtmlParserNode decls << decl; break; } - // Delibrate fall through + Q_FALLTHROUGH(); case Html_center: case Html_td: decl = QCss::Declaration(); @@ -1969,7 +1969,7 @@ QVector standardDeclarationForNode(const QTextHtmlParserNode } if (node.id != Html_pre) break; - // Delibrate fall through + Q_FALLTHROUGH(); case Html_br: case Html_nobr: decl = QCss::Declaration(); diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp index 7b6f830333..52b9dd9169 100644 --- a/src/network/access/qftp.cpp +++ b/src/network/access/qftp.cpp @@ -1121,7 +1121,7 @@ bool QFtpPI::processReply() case Success: // success handling state = Idle; - // no break! + Q_FALLTHROUGH(); case Idle: if (dtp.hasError()) { emit error(QFtp::UnknownError, dtp.errorMessage()); diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 3a780f636b..87f88aad5f 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -1036,7 +1036,7 @@ void QHttpNetworkConnectionChannel::_q_encrypted() if (!protocolHandler) { switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) { - case QSslConfiguration::NextProtocolNegotiationNegotiated: /* fall through */ + case QSslConfiguration::NextProtocolNegotiationNegotiated: case QSslConfiguration::NextProtocolNegotiationUnsupported: { QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol(); if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) { @@ -1056,6 +1056,7 @@ void QHttpNetworkConnectionChannel::_q_encrypted() "detected unknown Next Protocol Negotiation protocol"); break; } + Q_FALLTHROUGH(); } case QSslConfiguration::NextProtocolNegotiationNone: protocolHandler.reset(new QHttpProtocolHandler(this)); diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp index b486b75449..37e8b9bed8 100644 --- a/src/network/access/qhttpprotocolhandler.cpp +++ b/src/network/access/qhttpprotocolhandler.cpp @@ -99,7 +99,7 @@ void QHttpProtocolHandler::_q_receiveReply() switch (state) { case QHttpNetworkReplyPrivate::NothingDoneState: { m_reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; - // fallthrough + Q_FALLTHROUGH(); } case QHttpNetworkReplyPrivate::ReadingStatusState: { qint64 statusBytes = m_reply->d_func()->readStatus(m_socket); @@ -213,7 +213,8 @@ void QHttpProtocolHandler::_q_receiveReply() if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) break; - // everything done, fall through + // everything done + Q_FALLTHROUGH(); } case QHttpNetworkReplyPrivate::AllDoneState: m_channel->allDone(); @@ -428,7 +429,7 @@ bool QHttpProtocolHandler::sendRequest() } case QHttpNetworkConnectionChannel::ReadingState: // ignore _q_bytesWritten in these states - // fall through + Q_FALLTHROUGH(); default: break; } diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp index 7a538cbf08..b7cf989477 100644 --- a/src/network/access/qnetworkcookie.cpp +++ b/src/network/access/qnetworkcookie.cpp @@ -644,7 +644,7 @@ static QDateTime parseDateString(const QByteArray &dateString) switch (end - 1) { case 4: minutes = atoi(dateString.mid(at + 3, 2).constData()); - // fall through + Q_FALLTHROUGH(); case 2: hours = atoi(dateString.mid(at + 1, 2).constData()); break; diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp index 95f90af49e..7e3d2c5d6e 100644 --- a/src/network/kernel/qhostaddress.cpp +++ b/src/network/kernel/qhostaddress.cpp @@ -536,7 +536,7 @@ QHostAddress::QHostAddress(SpecialAddress address) case LocalHostIPv6: ip6[15] = 1; - // fall through + Q_FALLTHROUGH(); case AnyIPv6: d->setAddress(ip6); return; diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 9ae2d8ba8f..5a9641a9fe 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -712,7 +712,7 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin if (errorDetected) break; - // fall through + Q_FALLTHROUGH(); } case WSAEINPROGRESS: setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString); diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 518ec21f90..a1a8e4649d 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -618,7 +618,7 @@ void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, const QString QSocks5SocketEngine::tr("Connection to proxy timed out")); break; } - /* fall through */ + Q_FALLTHROUGH(); default: q->setError(controlSocketError, data->controlSocket->errorString()); break; @@ -1205,7 +1205,7 @@ void QSocks5SocketEnginePrivate::_q_controlSocketReadNotification() break; } - // fall through + Q_FALLTHROUGH(); default: qWarning("QSocks5SocketEnginePrivate::_q_controlSocketReadNotification: " "Unexpectedly received data while in state=%d and mode=%d", diff --git a/src/platformsupport/fbconvenience/qfbvthandler.cpp b/src/platformsupport/fbconvenience/qfbvthandler.cpp index 4c4a01a82e..2429cb7c5c 100644 --- a/src/platformsupport/fbconvenience/qfbvthandler.cpp +++ b/src/platformsupport/fbconvenience/qfbvthandler.cpp @@ -171,7 +171,7 @@ void QFbVtHandler::handleSignal() char sigNo; if (QT_READ(m_sigFd[1], &sigNo, sizeof(sigNo)) == sizeof(sigNo)) { switch (sigNo) { - case SIGINT: // fallthrough + case SIGINT: case SIGTERM: handleInt(); break; diff --git a/src/plugins/bearer/qnetworksession_impl.cpp b/src/plugins/bearer/qnetworksession_impl.cpp index 426cca139d..85942b56f1 100644 --- a/src/plugins/bearer/qnetworksession_impl.cpp +++ b/src/plugins/bearer/qnetworksession_impl.cpp @@ -111,10 +111,10 @@ void QNetworkSessionPrivateImpl::syncStateWithInterface() case QNetworkConfiguration::ServiceNetwork: serviceConfig = publicConfig; // Defer setting engine and signals until open(). - // fall through + Q_FALLTHROUGH(); case QNetworkConfiguration::UserChoice: // Defer setting serviceConfig and activeConfig until open(). - // fall through + Q_FALLTHROUGH(); default: engine = 0; } diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp index 3949113240..b9009fe704 100644 --- a/src/plugins/platforms/android/qandroidplatformtheme.cpp +++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp @@ -489,7 +489,7 @@ QVariant QAndroidPlatformTheme::themeHint(ThemeHint hint) const if (ret > 0) return ret; - // fall through + Q_FALLTHROUGH(); } default: return QPlatformTheme::themeHint(hint); diff --git a/src/plugins/platforms/cocoa/qprintengine_mac.mm b/src/plugins/platforms/cocoa/qprintengine_mac.mm index 3d0c91c36c..0b5b06c44f 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qprintengine_mac.mm @@ -200,7 +200,7 @@ int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const val = (int)resolution.vRes; break; } - //otherwise fall through + Q_FALLTHROUGH(); } case QPaintDevice::PdmDpiY: val = (int)d->resolution.vRes; diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 730d5de450..0ea2fcfa74 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -563,7 +563,7 @@ public: if (newPen.widthF() <= 1.0) props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT; - // fall through + Q_FALLTHROUGH(); default: props.dashStyle = D2D1_DASH_STYLE_CUSTOM; break; diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 3a683bd7a0..d4cdf3ef3c 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -945,7 +945,8 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, switch (et) { case QtWindows::KeyboardLayoutChangeEvent: if (QWindowsInputContext *wic = windowsInputContext()) - wic->handleInputLanguageChanged(wParam, lParam); // fallthrough intended. + wic->handleInputLanguageChanged(wParam, lParam); + Q_FALLTHROUGH(); case QtWindows::KeyDownEvent: case QtWindows::KeyEvent: case QtWindows::InputMethodKeyEvent: diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 79befc56c2..841464391d 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -551,14 +551,14 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &pixmapSiz break; case FileLinkIcon: stockFlags = SHGSI_LINKOVERLAY; - // Fall through + Q_FALLTHROUGH(); case FileIcon: stockId = SIID_DOCNOASSOC; resourceId = 1; break; case DirLinkIcon: stockFlags = SHGSI_LINKOVERLAY; - // Fall through + Q_FALLTHROUGH(); case DirClosedIcon: case DirIcon: stockId = SIID_FOLDER; @@ -572,7 +572,7 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &pixmapSiz break; case DirLinkOpenIcon: stockFlags = SHGSI_LINKOVERLAY; - // Fall through + Q_FALLTHROUGH(); case DirOpenIcon: stockId = SIID_FOLDEROPEN; resourceId = 5; diff --git a/src/printsupport/kernel/qprintengine_pdf.cpp b/src/printsupport/kernel/qprintengine_pdf.cpp index ed0422468d..278bb044e1 100644 --- a/src/printsupport/kernel/qprintengine_pdf.cpp +++ b/src/printsupport/kernel/qprintengine_pdf.cpp @@ -150,7 +150,7 @@ void QPdfPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va else d->m_pageLayout.setMode(QPageLayout::StandardMode); break; - case PPK_CopyCount: // fallthrough + case PPK_CopyCount: case PPK_NumberOfCopies: d->copies = value.toInt(); break; diff --git a/src/printsupport/kernel/qprintengine_win.cpp b/src/printsupport/kernel/qprintengine_win.cpp index b3dc33895b..b0f85dc825 100644 --- a/src/printsupport/kernel/qprintengine_win.cpp +++ b/src/printsupport/kernel/qprintengine_win.cpp @@ -1141,7 +1141,7 @@ void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant & #endif // QT_DEBUG_METRICS break; - case PPK_CopyCount: // fallthrough + case PPK_CopyCount: case PPK_NumberOfCopies: if (!d->devMode) break; diff --git a/src/testlib/qtestmouse.h b/src/testlib/qtestmouse.h index 632b801221..1143361323 100644 --- a/src/testlib/qtestmouse.h +++ b/src/testlib/qtestmouse.h @@ -115,14 +115,14 @@ namespace QTest case MouseDClick: qt_handleMouseEvent(w, pos, global, button, stateKey, ++lastMouseTimestamp); qt_handleMouseEvent(w, pos, global, Qt::NoButton, stateKey, ++lastMouseTimestamp); - // fall through + Q_FALLTHROUGH(); case MousePress: case MouseClick: qt_handleMouseEvent(w, pos, global, button, stateKey, ++lastMouseTimestamp); lastMouseButton = button; if (action == MousePress) break; - // fall through + Q_FALLTHROUGH(); case MouseRelease: qt_handleMouseEvent(w, pos, global, Qt::NoButton, stateKey, ++lastMouseTimestamp); lastMouseTimestamp += 500; // avoid double clicks being generated diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 87fb1318f9..afe64bb889 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -163,7 +163,7 @@ Type Moc::parseType() case SIGNED: case UNSIGNED: hasSignedOrUnsigned = true; - // fall through + Q_FALLTHROUGH(); case CONST: case VOLATILE: type.name += lexem(); diff --git a/src/tools/moc/preprocessor.cpp b/src/tools/moc/preprocessor.cpp index 415003e6b1..5ce28d931b 100644 --- a/src/tools/moc/preprocessor.cpp +++ b/src/tools/moc/preprocessor.cpp @@ -251,7 +251,7 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso } token = FLOATING_LITERAL; ++data; - // fall through + Q_FALLTHROUGH(); case FLOATING_LITERAL: while (is_digit_char(*data)) ++data; @@ -321,7 +321,7 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso ++data; } token = WHITESPACE; // one comment, one whitespace - // fall through; + Q_FALLTHROUGH(); case WHITESPACE: if (column == 1) column = 0; @@ -428,7 +428,7 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso } token = PP_FLOATING_LITERAL; ++data; - // fall through + Q_FALLTHROUGH(); case PP_FLOATING_LITERAL: while (is_digit_char(*data)) ++data; @@ -482,7 +482,7 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso ++data; } token = PP_WHITESPACE; // one comment, one whitespace - // fall through; + Q_FALLTHROUGH(); case PP_WHITESPACE: while (*data && (*data == ' ' || *data == '\t')) ++data; @@ -1177,7 +1177,7 @@ void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) case PP_ELIF: case PP_ELSE: skipUntilEndif(); - // fall through + Q_FALLTHROUGH(); case PP_ENDIF: until(PP_NEWLINE); continue; diff --git a/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp index 083187555d..26e7b086d9 100644 --- a/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp +++ b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp @@ -335,28 +335,28 @@ static void parseCmdLine(QStringList &arguments) switch (c) { case 'P': flags |= QDBusConnection::ExportNonScriptableProperties; - // fall through + Q_FALLTHROUGH(); case 'p': flags |= QDBusConnection::ExportScriptableProperties; break; case 'S': flags |= QDBusConnection::ExportNonScriptableSignals; - // fall through + Q_FALLTHROUGH(); case 's': flags |= QDBusConnection::ExportScriptableSignals; break; case 'M': flags |= QDBusConnection::ExportNonScriptableSlots; - // fall through + Q_FALLTHROUGH(); case 'm': flags |= QDBusConnection::ExportScriptableSlots; break; case 'A': flags |= QDBusConnection::ExportNonScriptableContents; - // fall through + Q_FALLTHROUGH(); case 'a': flags |= QDBusConnection::ExportScriptableContents; break; diff --git a/src/widgets/dialogs/qmessagebox.cpp b/src/widgets/dialogs/qmessagebox.cpp index 689704884f..96046b7ff4 100644 --- a/src/widgets/dialogs/qmessagebox.cpp +++ b/src/widgets/dialogs/qmessagebox.cpp @@ -1379,7 +1379,7 @@ void QMessageBox::changeEvent(QEvent *ev) d->buttonBox->setCenterButtons(style()->styleHint(QStyle::SH_MessageBox_CenterButtons, 0, this)); if (d->informativeLabel) d->informativeLabel->setTextInteractionFlags(flags); - // intentional fall through + Q_FALLTHROUGH(); } case QEvent::FontChange: case QEvent::ApplicationFontChange: @@ -1390,6 +1390,7 @@ void QMessageBox::changeEvent(QEvent *ev) d->label->setFont(f); } #endif + Q_FALLTHROUGH(); default: break; } diff --git a/src/widgets/graphicsview/qgraphicsitem.cpp b/src/widgets/graphicsview/qgraphicsitem.cpp index 4b5e380f4b..5492862287 100644 --- a/src/widgets/graphicsview/qgraphicsitem.cpp +++ b/src/widgets/graphicsview/qgraphicsitem.cpp @@ -9570,7 +9570,7 @@ public: shape = qt_regionToPath(QRegion(mask).translated(offset.toPoint())); break; } - // FALL THROUGH + Q_FALLTHROUGH(); } case QGraphicsPixmapItem::BoundingRectShape: shape.addRect(QRectF(offset.x(), offset.y(), pixmap.width(), pixmap.height())); diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 7b393463a6..6f1e301d4f 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -2563,7 +2563,7 @@ void QHeaderView::mouseReleaseEvent(QMouseEvent *e) int section = logicalIndexAt(pos); updateSection(section); } - // fall through + Q_FALLTHROUGH(); case QHeaderViewPrivate::NoState: if (d->clickableSections) { int section = logicalIndexAt(pos); @@ -2669,7 +2669,7 @@ bool QHeaderView::viewportEvent(QEvent *e) case QEvent::FontChange: case QEvent::StyleChange: d->invalidateCachedSizeHint(); - // Fall through + Q_FALLTHROUGH(); case QEvent::Hide: case QEvent::Show: { QAbstractScrollArea *parent = qobject_cast(parentWidget()); diff --git a/src/widgets/itemviews/qtreewidget.cpp b/src/widgets/itemviews/qtreewidget.cpp index 74ca7d2827..5e0f13277c 100644 --- a/src/widgets/itemviews/qtreewidget.cpp +++ b/src/widgets/itemviews/qtreewidget.cpp @@ -1737,7 +1737,7 @@ void QTreeWidgetItem::setData(int column, int role, const QVariant &value) } } } - // Don't break, but fall through + Q_FALLTHROUGH(); default: if (column < values.count()) { bool found = false; @@ -1787,7 +1787,7 @@ QVariant QTreeWidgetItem::data(int column, int role) const // special case for check state in tristate if (children.count() && (itemFlags & Qt::ItemIsAutoTristate)) return childrenCheckState(column); - // fallthrough intended + Q_FALLTHROUGH(); default: if (column >= 0 && column < values.size()) { const QVector &column_values = values.at(column); diff --git a/src/widgets/kernel/qwhatsthis.cpp b/src/widgets/kernel/qwhatsthis.cpp index 66b622911a..32fe5f5e64 100644 --- a/src/widgets/kernel/qwhatsthis.cpp +++ b/src/widgets/kernel/qwhatsthis.cpp @@ -450,8 +450,8 @@ bool QWhatsThisPrivate::eventFilter(QObject *o, QEvent *e) QApplication::changeOverrideCursor((!sentEvent || !e.isAccepted())? Qt::ForbiddenCursor:Qt::WhatsThisCursor); #endif + Q_FALLTHROUGH(); } - // fall through case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: if (leaveOnMouseRelease && e->type() == QEvent::MouseButtonRelease) diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 25874e9bcc..e0b8024f9a 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -8835,7 +8835,7 @@ bool QWidget::event(QEvent *event) case QEvent::KeyRelease: keyReleaseEvent((QKeyEvent*)event); - // fall through + Q_FALLTHROUGH(); case QEvent::ShortcutOverride: break; @@ -8954,7 +8954,7 @@ bool QWidget::event(QEvent *event) 0, this); break; } - // fall through + Q_FALLTHROUGH(); #endif default: event->ignore(); @@ -9193,7 +9193,7 @@ bool QWidget::event(QEvent *event) } if (windowHandle() && !qstrncmp(propName, "_q_platform_", 12)) windowHandle()->setProperty(propName, property(propName)); - // fall through + Q_FALLTHROUGH(); } #endif default: @@ -11194,13 +11194,13 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) d->enforceNativeChildren(); } #endif - // fall through + Q_FALLTHROUGH(); case Qt::WA_OpaquePaintEvent: d->updateIsOpaque(); break; case Qt::WA_NoSystemBackground: d->updateIsOpaque(); - // fall through... + Q_FALLTHROUGH(); case Qt::WA_UpdatesDisabled: d->updateSystemBackground(); break; diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 6cdfc061e1..140f0ad2f3 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -4232,7 +4232,6 @@ QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast(opt)) { switch (sc) { case SC_GroupBoxFrame: - // FALL THROUGH case SC_GroupBoxContents: { int topMargin = 0; int topHeight = 0; @@ -4261,7 +4260,6 @@ QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex break; } case SC_GroupBoxCheckBox: - // FALL THROUGH case SC_GroupBoxLabel: { QFontMetrics fontMetrics = groupBox->fontMetrics; int h = fontMetrics.height(); @@ -4327,7 +4325,7 @@ QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex if (numSubControls == 1) break; offset += buttonWidth + 2; - //FALL THROUGH + Q_FALLTHROUGH(); case SC_MdiNormalButton: // No offset needed if // 1) There's only one sub control @@ -4895,7 +4893,7 @@ QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, case CT_ProgressBar: case CT_TabBarTab: // just return the contentsSize for now - // fall through intended + Q_FALLTHROUGH(); default: break; } diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index 44555c3058..c5904a5a96 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -1100,7 +1100,7 @@ void QRenderRule::fixupBorder(int nativeWidth) case BorderStyle_Native: if (bd->borders[i] == 0) bd->borders[i] = nativeWidth; - // intentional fall through + Q_FALLTHROUGH(); default: if (bd->colors[i].style() == Qt::NoBrush) // auto-acquire 'color' bd->colors[i] = color; diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp index c3a50968e2..e3bf28608c 100644 --- a/src/widgets/styles/qwindowsstyle.cpp +++ b/src/widgets/styles/qwindowsstyle.cpp @@ -808,7 +808,8 @@ void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, p->setPen(opt->palette.dark().color()); else p->setPen(opt->palette.text().color()); - } // Fall through! + } + Q_FALLTHROUGH(); case PE_IndicatorViewItemCheck: if (!doRestore) { p->save(); @@ -2383,7 +2384,7 @@ QSize QWindowsStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, case CT_ToolButton: if (qstyleoption_cast(opt)) return sz += QSize(7, 6); - // Otherwise, fall through + Q_FALLTHROUGH(); default: sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget); diff --git a/src/widgets/styles/qwindowsxpstyle.cpp b/src/widgets/styles/qwindowsxpstyle.cpp index ffedc37edb..f3a8544d96 100644 --- a/src/widgets/styles/qwindowsxpstyle.cpp +++ b/src/widgets/styles/qwindowsxpstyle.cpp @@ -3465,13 +3465,13 @@ QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionCompl case SC_TitleBarContextHelpButton: if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) offset += delta; - //fall through + Q_FALLTHROUGH(); case SC_TitleBarMinButton: if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) offset += delta; else if (subControl == SC_TitleBarMinButton) break; - //fall through + Q_FALLTHROUGH(); case SC_TitleBarNormalButton: if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) offset += delta; @@ -3479,25 +3479,25 @@ QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionCompl offset += delta; else if (subControl == SC_TitleBarNormalButton) break; - //fall through + Q_FALLTHROUGH(); case SC_TitleBarMaxButton: if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) offset += delta; else if (subControl == SC_TitleBarMaxButton) break; - //fall through + Q_FALLTHROUGH(); case SC_TitleBarShadeButton: if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) offset += delta; else if (subControl == SC_TitleBarShadeButton) break; - //fall through + Q_FALLTHROUGH(); case SC_TitleBarUnshadeButton: if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) offset += delta; else if (subControl == SC_TitleBarUnshadeButton) break; - //fall through + Q_FALLTHROUGH(); case SC_TitleBarCloseButton: if (tb->titleBarFlags & Qt::WindowSystemMenuHint) offset += delta; @@ -3576,7 +3576,7 @@ QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionCompl if (numSubControls == 1) break; offset += buttonWidth; - //FALL THROUGH + Q_FALLTHROUGH(); case SC_MdiNormalButton: // No offset needed if // 1) There's only one sub control diff --git a/src/widgets/util/qflickgesture.cpp b/src/widgets/util/qflickgesture.cpp index 4f08664784..57db9ff7fc 100644 --- a/src/widgets/util/qflickgesture.cpp +++ b/src/widgets/util/qflickgesture.cpp @@ -551,11 +551,11 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state, case QEvent::TouchBegin: inputType = QScroller::InputPress; - // fall through + Q_FALLTHROUGH(); case QEvent::TouchEnd: if (!inputType) inputType = QScroller::InputRelease; - // fallthrough + Q_FALLTHROUGH(); case QEvent::TouchUpdate: if (!inputType) inputType = QScroller::InputMove; @@ -669,7 +669,7 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state, event->accept(); } } - // fall through + Q_FALLTHROUGH(); case QEvent::TouchBegin: q->setHotSpot(globalPos); result |= scrollerIsActive ? TriggerGesture : MayBeGesture; @@ -681,7 +681,7 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state, #endif if (PressDelayHandler::instance()->isDelaying()) result |= ConsumeEventHint; - // fall through + Q_FALLTHROUGH(); case QEvent::TouchUpdate: result |= scrollerIsActive ? TriggerGesture : Ignore; break; @@ -692,7 +692,7 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state, case QEvent::MouseButtonRelease: if (PressDelayHandler::instance()->released(event, scrollerWasDragging || scrollerWasScrolling, scrollerIsActive)) result |= ConsumeEventHint; - // fall through + Q_FALLTHROUGH(); case QEvent::TouchEnd: result |= scrollerIsActive ? FinishGesture : CancelGesture; break; diff --git a/src/widgets/widgets/qabstractbutton.cpp b/src/widgets/widgets/qabstractbutton.cpp index 1a5b8db33c..d04ee24861 100644 --- a/src/widgets/widgets/qabstractbutton.cpp +++ b/src/widgets/widgets/qabstractbutton.cpp @@ -1058,7 +1058,7 @@ void QAbstractButton::keyPressEvent(QKeyEvent *e) break; case Qt::Key_Up: next = false; - // fall through + Q_FALLTHROUGH(); case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Down: { diff --git a/src/widgets/widgets/qabstractslider.cpp b/src/widgets/widgets/qabstractslider.cpp index 4221ff40ef..e85d82edb8 100644 --- a/src/widgets/widgets/qabstractslider.cpp +++ b/src/widgets/widgets/qabstractslider.cpp @@ -933,7 +933,7 @@ void QAbstractSlider::changeEvent(QEvent *ev) d->repeatActionTimer.stop(); setSliderDown(false); } - // fall through... + Q_FALLTHROUGH(); default: QWidget::changeEvent(ev); } diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index af178ce8f5..693d5f9e93 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -676,7 +676,7 @@ bool QComboBoxPrivateContainer::eventFilter(QObject *o, QEvent *e) case Qt::Key_Down: if (!(keyEvent->modifiers() & Qt::AltModifier)) break; - // fall through + Q_FALLTHROUGH(); case Qt::Key_F4: combo->hidePopup(); return true; @@ -3140,7 +3140,7 @@ void QComboBox::keyPressEvent(QKeyEvent *e) return; } else if (e->modifiers() & Qt::ControlModifier) break; // pass to line edit for auto completion - // fall through + Q_FALLTHROUGH(); case Qt::Key_PageDown: #ifdef QT_KEYPAD_NAVIGATION if (QApplication::keypadNavigationEnabled()) diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index cb99b58d97..2131025c8d 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -2979,7 +2979,7 @@ void QMenu::keyPressEvent(QKeyEvent *e) key_consumed = true; break; } - //FALL THROUGH + Q_FALLTHROUGH(); case Qt::Key_Left: { if (d->currentAction && !d->scroll) { QAction *nextAction = 0; @@ -3026,6 +3026,7 @@ void QMenu::keyPressEvent(QKeyEvent *e) if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this)) break; // for motif, fall through + Q_FALLTHROUGH(); #ifdef QT_KEYPAD_NAVIGATION case Qt::Key_Select: #endif -- cgit v1.2.3 From 057793a841bfa6dc61b35fd9d8ea92570d46b6c1 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Mon, 22 Aug 2016 16:37:31 +0300 Subject: qdbusxmlgenerator.cpp: reduce allocations in typeNameToXml() Don't create QString from QL1S, just iterate over QL1S directly. Change-Id: I35196991224c206841efb11894a9baf736ead97e Reviewed-by: Marc Mutz Reviewed-by: Edward Welbourne Reviewed-by: Thiago Macieira --- src/dbus/qdbusxmlgenerator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dbus/qdbusxmlgenerator.cpp b/src/dbus/qdbusxmlgenerator.cpp index 7e7ce9a7e6..d239a69f73 100644 --- a/src/dbus/qdbusxmlgenerator.cpp +++ b/src/dbus/qdbusxmlgenerator.cpp @@ -59,10 +59,10 @@ static inline QString typeNameToXml(const char *typeName) { // ### copied from qtextdocument.cpp // ### move this into Qt Core at some point - QString plain = QLatin1String(typeName); + const QLatin1String plain(typeName); QString rich; - rich.reserve(int(plain.length() * 1.1)); - for (int i = 0; i < plain.length(); ++i) { + rich.reserve(int(plain.size() * 1.1)); + for (int i = 0; i < plain.size(); ++i) { if (plain.at(i) == QLatin1Char('<')) rich += QLatin1String("<"); else if (plain.at(i) == QLatin1Char('>')) -- cgit v1.2.3 From 95c767f5be3cd42b4ed79eeed6fd4aa6cc29dc16 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 22 Aug 2016 16:29:28 +0200 Subject: Windows Font Engine: Fix MinGW developer build Suppress warning: qwindowsfontengine.cpp:590:26: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare] Amends change ac1e87d9f373ad649d989f254b37d2f29ddf25fe. Change-Id: Ibd25c522aac8e946c5e0759ea20e023883622914 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/plugins/platforms/windows/qwindowsfontengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 12340182af..f5d1ed9fad 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -587,7 +587,7 @@ namespace { QFixed QWindowsFontEngine::capHeight() const { const QByteArray tableData = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); - if (tableData.size() >= sizeof(OS2Table)) { + if (size_t(tableData.size()) >= sizeof(OS2Table)) { const OS2Table *table = reinterpret_cast(tableData.constData()); if (qFromBigEndian(table->version) >= 2) { qint16 capHeight = qFromBigEndian(table->capHeight); -- cgit v1.2.3 From 660c34077eb97c3aa49671fe555ecca717933e0d Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 23 Aug 2016 12:25:05 +0200 Subject: DirectWrite: Fix crash when embedding empty glyphs in PDF Empty QPainterPaths can have one move-to element, so the test to detect this case was incomplete. For empty glyphs returned from DirectWrite this would trigger a crash. This is only reproducible after also enabling embedding of fonts with the DirectWrite engine. Task-number: QTBUG-54740 Change-Id: I3fbbfb8d958818550d5ab5234242d39688b84811 Reviewed-by: Lars Knoll --- src/gui/text/qfontsubset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/text/qfontsubset.cpp b/src/gui/text/qfontsubset.cpp index 49638832e0..92eeaf7919 100644 --- a/src/gui/text/qfontsubset.cpp +++ b/src/gui/text/qfontsubset.cpp @@ -966,7 +966,7 @@ static QTtfGlyph generateGlyph(int index, const QPainterPath &path, qreal advanc glyph.advanceWidth = qRound(advance * 2048. / ppem); glyph.lsb = qRound(lsb * 2048. / ppem); - if (!path.elementCount()) { + if (path.isEmpty()) { //qDebug("glyph %d is empty", index); lsb = 0; glyph.xMin = glyph.xMax = glyph.yMin = glyph.yMax = 0; -- cgit v1.2.3 From 8de67524961d2ebae3777dbfc54fe8cea7300cbc Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Tue, 23 Aug 2016 12:16:47 -0700 Subject: Fix issue causing pch configure test to always fail This fixes a regression introduced in 4a1bafcc4ee5b7d968620808e155c1617aa6f273. Change-Id: Iafeec043c39af79cee8b53cf74dcd452c03a4826 Reviewed-by: Oswald Buddenhagen Reviewed-by: Thiago Macieira --- config.tests/common/pch/pch.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/config.tests/common/pch/pch.pro b/config.tests/common/pch/pch.pro index a6f842dff0..f6384b71e1 100644 --- a/config.tests/common/pch/pch.pro +++ b/config.tests/common/pch/pch.pro @@ -1,3 +1,4 @@ CONFIG += precompile_header +PRECOMPILED_DIR = .pch PRECOMPILED_HEADER = header.h SOURCES = source.cpp -- cgit v1.2.3 From e83f6aaaa6b1caa59e4dd2c304439e1fb2572d71 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 23 Dec 2015 13:26:32 +0100 Subject: QMimeProvider: fix quadratic loop Calling QMutableListIterator::remove() in a loop constitutes quadratic behavior (O(N) function called O(N) times). Fix by splitting the loop, simplifying it by sharing conditions, and using std::remove_if(), which is linear. Removes one more use of mutable Java iterators. Change-Id: I88bde414777b50996e546bc8cb238619ea4fb645 Reviewed-by: Edward Welbourne Reviewed-by: David Faure --- src/corelib/mimetypes/qmimeprovider.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp index e72f9a30d0..e25eb4272d 100644 --- a/src/corelib/mimetypes/qmimeprovider.cpp +++ b/src/corelib/mimetypes/qmimeprovider.cpp @@ -217,22 +217,24 @@ bool QMimeBinaryProvider::isValid() bool QMimeBinaryProvider::CacheFileList::checkCacheChanged() { bool somethingChanged = false; - QMutableListIterator it(*this); - while (it.hasNext()) { - CacheFile *cacheFile = it.next(); + for (CacheFile *cacheFile : qAsConst(*this)) { QFileInfo fileInfo(cacheFile->file); - if (!fileInfo.exists()) { // This can't happen by just running update-mime-database. But the user could use rm -rf :-) - delete cacheFile; - it.remove(); - somethingChanged = true; - } else if (fileInfo.lastModified() > cacheFile->m_mtime) { - if (!cacheFile->reload()) { - delete cacheFile; - it.remove(); - } + if (!fileInfo.exists() || fileInfo.lastModified() > cacheFile->m_mtime) { + // Deletion can't happen by just running update-mime-database. + // But the user could use rm -rf :-) + cacheFile->reload(); // will mark itself as invalid on failure somethingChanged = true; } } + if (somethingChanged) { + auto deleteIfNoLongerValid = [](CacheFile *cacheFile) -> bool { + const bool invalid = !cacheFile->isValid(); + if (invalid) + delete cacheFile; + return invalid; + }; + erase(std::remove_if(begin(), end(), deleteIfNoLongerValid), end()); + } return somethingChanged; } -- cgit v1.2.3