diff options
-rw-r--r-- | src/quick/items/items.pri | 2 | ||||
-rw-r--r-- | src/quick/items/qquickborderimage.cpp | 70 | ||||
-rw-r--r-- | src/quick/items/qquickimage.cpp | 11 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 113 | ||||
-rw-r--r-- | src/quick/items/qquickitem.h | 5 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 4 | ||||
-rw-r--r-- | src/quick/items/qquickninepatchnode.cpp | 305 | ||||
-rw-r--r-- | src/quick/items/qquickninepatchnode_p.h | 98 | ||||
-rw-r--r-- | src/quick/items/qquickrectangle.cpp | 1 | ||||
-rw-r--r-- | src/quick/items/qquickshadereffectsource.cpp | 2 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgmaterial.h | 5 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgadaptationlayer_p.h | 12 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgcontext.cpp | 1 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgdefaultimagenode.cpp | 605 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgdefaultimagenode_p.h | 26 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgdefaultrectanglenode.cpp | 854 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgdefaultrectanglenode_p.h | 35 | ||||
-rw-r--r-- | src/quick/scenegraph/util/qsgtexturematerial.cpp | 18 | ||||
-rw-r--r-- | src/quick/scenegraph/util/qsgtexturematerial_p.h | 14 |
19 files changed, 1316 insertions, 865 deletions
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 4f22b32837..9f205ed503 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -30,7 +30,6 @@ HEADERS += \ $$PWD/qquickimage_p_p.h \ $$PWD/qquickborderimage_p.h \ $$PWD/qquickborderimage_p_p.h \ - $$PWD/qquickninepatchnode_p.h \ $$PWD/qquickscalegrid_p_p.h \ $$PWD/qquickmousearea_p.h \ $$PWD/qquickmousearea_p_p.h \ @@ -96,7 +95,6 @@ SOURCES += \ $$PWD/qquickimagebase.cpp \ $$PWD/qquickimage.cpp \ $$PWD/qquickborderimage.cpp \ - $$PWD/qquickninepatchnode.cpp \ $$PWD/qquickscalegrid.cpp \ $$PWD/qquickmousearea.cpp \ $$PWD/qquickpincharea.cpp \ diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp index abd20c66f0..889877aea1 100644 --- a/src/quick/items/qquickborderimage.cpp +++ b/src/quick/items/qquickborderimage.cpp @@ -41,7 +41,6 @@ #include "qquickborderimage_p.h" #include "qquickborderimage_p_p.h" -#include "qquickninepatchnode_p.h" #include <QtQml/qqmlinfo.h> #include <QtQml/qqmlfile.h> @@ -565,29 +564,70 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat return 0; } - QQuickNinePatchNode *node = static_cast<QQuickNinePatchNode *>(oldNode); + QSGImageNode *node = static_cast<QSGImageNode *>(oldNode); - if (!node) { - node = new QQuickNinePatchNode(); - } + if (!node) + node = d->sceneGraphContext()->createImageNode(); node->setTexture(texture); // Don't implicitly create the scalegrid in the rendering thread... + QRectF innerSourceRect(0, 0, 1, 1); + QRectF targetRect(0, 0, width(), height()); + QRectF innerTargetRect = targetRect; if (d->border) { const QQuickScaleGrid *border = d->getScaleGrid(); - node->setInnerRect(QRectF(border->left(), - border->top(), - qMax(1, d->pix.width() - border->right() - border->left()), - qMax(1, d->pix.height() - border->bottom() - border->top()))); - } else { - node->setInnerRect(QRectF(0, 0, d->pix.width(), d->pix.height())); + innerSourceRect = QRectF(border->left() / qreal(d->pix.width()), + border->top() / qreal(d->pix.height()), + qMax<qreal>(0, d->pix.width() - border->right() - border->left()) / d->pix.width(), + qMax<qreal>(0, d->pix.height() - border->bottom() - border->top()) / d->pix.height()); + innerTargetRect = QRectF(border->left(), + border->top(), + qMax<qreal>(0, width() - border->right() - border->left()), + qMax<qreal>(0, height() - border->bottom() - border->top())); } - node->setRect(QRectF(0, 0, width(), height())); - node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest); - node->setHorzontalTileMode(d->horizontalTileMode); - node->setVerticalTileMode(d->verticalTileMode); + qreal hTiles = 1; + qreal vTiles = 1; + if (innerSourceRect.width() != 0) { + switch (d->horizontalTileMode) { + case QQuickBorderImage::Repeat: + hTiles = innerTargetRect.width() / qreal(innerSourceRect.width() * d->pix.width()); + break; + case QQuickBorderImage::Round: + hTiles = qCeil(innerTargetRect.width() / qreal(innerSourceRect.width() * d->pix.width())); + break; + default: + break; + } + } + if (innerSourceRect.height() != 0) { + switch (d->verticalTileMode) { + case QQuickBorderImage::Repeat: + vTiles = innerTargetRect.height() / qreal(innerSourceRect.height() * d->pix.height()); + break; + case QQuickBorderImage::Round: + vTiles = qCeil(innerTargetRect.height() / qreal(innerSourceRect.height() * d->pix.height())); + break; + default: + break; + } + } + + node->setTargetRect(targetRect); + node->setInnerSourceRect(innerSourceRect); + node->setInnerTargetRect(innerTargetRect); + node->setSubSourceRect(QRectF(0, 0, hTiles, vTiles)); node->setMirror(d->mirror); + + node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest); + if (innerSourceRect == QRectF(0, 0, 1, 1) && (vTiles > 1 || hTiles > 1)) { + node->setHorizontalWrapMode(QSGTexture::Repeat); + node->setVerticalWrapMode(QSGTexture::Repeat); + } else { + node->setHorizontalWrapMode(QSGTexture::ClampToEdge); + node->setVerticalWrapMode(QSGTexture::ClampToEdge); + } + node->setAntialiasing(d->antialiasing); node->update(); return node; diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp index aa68477a9d..f4089be48c 100644 --- a/src/quick/items/qquickimage.cpp +++ b/src/quick/items/qquickimage.cpp @@ -674,18 +674,15 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) sourceRect.width() / d->pix.width(), sourceRect.height() / d->pix.height()); - if (d->mirror) { - qreal oldLeft = nsrect.left(); - nsrect.setLeft(nsrect.right()); - nsrect.setRight(oldLeft); - } - node->setHorizontalWrapMode(hWrap); node->setVerticalWrapMode(vWrap); node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest); node->setTargetRect(targetRect); - node->setSourceRect(nsrect); + node->setInnerTargetRect(targetRect); + node->setSubSourceRect(nsrect); + node->setMirror(d->mirror); + node->setAntialiasing(d->antialiasing); node->update(); return node; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index d3c8973653..a41e451209 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2401,26 +2401,53 @@ bool QQuickItem::isComponentComplete() const } QQuickItemPrivate::QQuickItemPrivate() -: _anchors(0), _stateGroup(0), - flags(0), widthValid(false), heightValid(false), baselineOffsetValid(false), componentComplete(true), - keepMouse(false), keepTouch(false), hoverEnabled(false), smooth(true), focus(false), activeFocus(false), notifiedFocus(false), - notifiedActiveFocus(false), filtersChildMouseEvents(false), explicitVisible(true), - effectiveVisible(true), explicitEnable(true), effectiveEnable(true), polishScheduled(false), - inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true), - inheritMirrorFromParent(false), inheritMirrorFromItem(false), - isAccessible(false), culled(false), - - dirtyAttributes(0), nextDirtyItem(0), prevDirtyItem(0), - - canvas(0), canvasRefCount(0), parentItem(0), sortedChildItems(&childItems), - - subFocusItem(0), - - x(0), y(0), width(0), height(0), implicitWidth(0), implicitHeight(0), - - baselineOffset(0), - - itemNodeInstance(0), groupNode(0), paintNode(0) + : _anchors(0) + , _stateGroup(0) + , flags(0) + , widthValid(false) + , heightValid(false) + , baselineOffsetValid(false) + , componentComplete(true) + , keepMouse(false) + , keepTouch(false) + , hoverEnabled(false) + , smooth(true) + , antialiasing(false) + , focus(false) + , activeFocus(false) + , notifiedFocus(false) + , notifiedActiveFocus(false) + , filtersChildMouseEvents(false) + , explicitVisible(true) + , effectiveVisible(true) + , explicitEnable(true) + , effectiveEnable(true) + , polishScheduled(false) + , inheritedLayoutMirror(false) + , effectiveLayoutMirror(false) + , isMirrorImplicit(true) + , inheritMirrorFromParent(false) + , inheritMirrorFromItem(false) + , isAccessible(false) + , culled(false) + , dirtyAttributes(0) + , nextDirtyItem(0) + , prevDirtyItem(0) + , canvas(0) + , canvasRefCount(0) + , parentItem(0) + , sortedChildItems(&childItems) + , subFocusItem(0) + , x(0) + , y(0) + , width(0) + , height(0) + , implicitWidth(0) + , implicitHeight(0) + , baselineOffset(0) + , itemNodeInstance(0) + , groupNode(0) + , paintNode(0) { } @@ -4321,6 +4348,7 @@ QString QQuickItemPrivate::dirtyToString() const DIRTY_TO_STRING(EffectReference); DIRTY_TO_STRING(Visible); DIRTY_TO_STRING(HideReference); + DIRTY_TO_STRING(Antialiasing); return rv; } @@ -4488,10 +4516,10 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt */ /*! - Returns true if the item should be drawn with antialiasing and + Returns true if the item should be drawn with smooth pixmap filtering, false otherwise. - The default is false. + The default is true. \sa setSmooth() */ @@ -4519,6 +4547,47 @@ void QQuickItem::setSmooth(bool smooth) emit smoothChanged(smooth); } +/*! + \property QQuickItem::antialiasing + \brief Specifies whether the item is antialiased or not + + Primarily used in Rectangle and image based elements to decide if the item should + use antialiasing or not. Items with antialiasing enabled require more memory and + are potentially slower to render. + + The default is false. +*/ + +/*! + Returns true if the item should be drawn with antialiasing, false otherwise. + + The default is false. + + \sa setAntialiasing() +*/ +bool QQuickItem::antialiasing() const +{ + Q_D(const QQuickItem); + return d->antialiasing; +} + +/*! + Sets whether the item should be drawn with antialiasing to \a antialiasing. + + \sa antialiasing() +*/ +void QQuickItem::setAntialiasing(bool antialiasing) +{ + Q_D(QQuickItem); + if (d->antialiasing == antialiasing) + return; + + d->antialiasing = antialiasing; + d->dirty(QQuickItemPrivate::Antialiasing); + + emit antialiasingChanged(antialiasing); +} + QQuickItem::Flags QQuickItem::flags() const { Q_D(const QQuickItem); diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 7174edd057..15bf5dd46d 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -142,6 +142,7 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus Q_PROPERTY(QQmlListProperty<QQuickTransform> transform READ transform DESIGNABLE false FINAL) Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) + Q_PROPERTY(bool antialiasing READ antialiasing WRITE setAntialiasing NOTIFY antialiasingChanged) Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged) Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged) @@ -258,6 +259,9 @@ public: bool smooth() const; void setSmooth(bool); + bool antialiasing() const; + void setAntialiasing(bool); + Flags flags() const; void setFlag(Flag flag, bool enabled = true); void setFlags(Flags flags); @@ -332,6 +336,7 @@ Q_SIGNALS: void parentChanged(QQuickItem *); void transformOriginChanged(TransformOrigin); void smoothChanged(bool); + void antialiasingChanged(bool); void clipChanged(bool); // XXX todo diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index ddebefdb40..69099212ea 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -389,6 +389,7 @@ public: bool keepTouch:1; bool hoverEnabled:1; bool smooth:1; + bool antialiasing:1; bool focus:1; bool activeFocus:1; bool notifiedFocus:1; @@ -431,13 +432,14 @@ public: EffectReference = 0x00008000, Visible = 0x00010000, HideReference = 0x00020000, + Antialiasing = 0x00040000, // When you add an attribute here, don't forget to update // dirtyToString() TransformUpdateMask = TransformOrigin | Transform | BasicTransform | Position | Size | Canvas, ComplexTransformUpdateMask = Transform | Canvas, - ContentUpdateMask = Size | Content | Smooth | Canvas, + ContentUpdateMask = Size | Content | Smooth | Canvas | Antialiasing, ChildrenUpdateMask = ChildrenChanged | ChildrenStackingChanged | EffectReference | Canvas }; diff --git a/src/quick/items/qquickninepatchnode.cpp b/src/quick/items/qquickninepatchnode.cpp deleted file mode 100644 index d304e2dece..0000000000 --- a/src/quick/items/qquickninepatchnode.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickninepatchnode_p.h" -#include <private/qsgadaptationlayer_p.h> -#include <private/qmath_p.h> - -QQuickNinePatchNode::QQuickNinePatchNode() - : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) - , m_horizontalTileMode(QQuickBorderImage::Stretch) - , m_verticalTileMode(QQuickBorderImage::Stretch) - , m_dirtyGeometry(false) - , m_mirror(false) -{ - m_geometry.setIndexDataPattern(QSGGeometry::StaticPattern); - m_geometry.setVertexDataPattern(QSGGeometry::StaticPattern); - - setOpaqueMaterial(&m_material); - setMaterial(&m_materialO); - setGeometry(&m_geometry); - m_geometry.setDrawingMode(GL_TRIANGLES); -#ifdef QML_RUNTIME_TESTING - description = QLatin1String("borderimage"); -#endif -} - -void QQuickNinePatchNode::setInnerRect(const QRectF &rect) -{ - if (m_innerRect == rect) - return; - m_innerRect = rect; - m_dirtyGeometry = true; -} - -void QQuickNinePatchNode::setRect(const QRectF &rect) -{ - if (m_targetRect == rect) - return; - m_targetRect = rect; - m_dirtyGeometry = true; -} - -void QQuickNinePatchNode::setHorzontalTileMode(QQuickBorderImage::TileMode mode) -{ - if (mode == QQuickBorderImage::TileMode(m_horizontalTileMode)) - return; - m_horizontalTileMode = mode; - m_dirtyGeometry = true; -} - - -void QQuickNinePatchNode::setVerticalTileMode(QQuickBorderImage::TileMode mode) -{ - if (mode == QQuickBorderImage::TileMode(m_verticalTileMode)) - return; - m_verticalTileMode = mode; - m_dirtyGeometry = true; -} - - -void QQuickNinePatchNode::setFiltering(QSGTexture::Filtering filtering) -{ - if (m_material.filtering() == filtering) - return; - - m_material.setFiltering(filtering); - m_materialO.setFiltering(filtering); - markDirty(DirtyMaterial); -} - -QSGTexture::Filtering QQuickNinePatchNode::filtering() const -{ - return m_material.filtering(); -} - -void QQuickNinePatchNode::setTexture(QSGTexture *texture) -{ - if (texture == m_material.texture()) - return; - m_material.setTexture(texture); - m_materialO.setTexture(texture); - markDirty(DirtyMaterial); -} - -QSGTexture *QQuickNinePatchNode::texture() const -{ - return m_material.texture(); -} - -void QQuickNinePatchNode::setMirror(bool m) -{ - if (m_mirror == m) - return; - m_mirror = m; - m_dirtyGeometry = true; -} - - -void QQuickNinePatchNode::update() -{ - if (!m_dirtyGeometry) - return; - - // For stretch this algorithm could be simplified to use less vertices - // as more vertices could be reused then, but I doubt its where our main - // problem will lie. This way, we at least share the algorithm between all - - Q_ASSERT(m_material.texture()); - - float tw = m_material.texture()->textureSize().width(); - float th = m_material.texture()->textureSize().height(); - - QRectF textureSubRect = m_material.texture()->normalizedTextureSubRect(); - QSize textureSize = m_material.texture()->textureSize(); - - float rightBorder = tw - m_innerRect.right(); - float bottomBorder = th - m_innerRect.bottom(); - -// qDebug() << m_innerRect << m_targetRect << m_horizontalTileMode << m_verticalTileMode; - - int xChunkCount = 0; // Number of chunks - float xChunkSize = 0; // Size of chunk in pixels - float xTexSize = m_innerRect.width(); // Size of the texture to stretch/tile - float xSize = m_targetRect.width() - m_innerRect.left() - rightBorder; // Size of area to fill with chunks - - if (m_horizontalTileMode == QQuickBorderImage::Repeat) { - xChunkCount = qCeil(xSize / xTexSize); - xChunkSize = xTexSize; - } else if (m_horizontalTileMode == QQuickBorderImage::Round) { - xChunkCount = qCeil(xSize / xTexSize); - qreal fullWidth = xChunkCount * xTexSize; - xChunkSize = xTexSize * xSize / fullWidth; - } else { - xChunkCount = 1; - xChunkSize = xSize; - } - - int yChunkCount = 0; - float yChunkSize = 0; // Relative to target rect. - float yTexSize = m_innerRect.height(); // Size of the texture to stretch/tile - float ySize = m_targetRect.height() - m_innerRect.top() - bottomBorder; - - if (m_verticalTileMode == QQuickBorderImage::Repeat) { - yChunkCount = qCeil(ySize / yTexSize); - yChunkSize = yTexSize; - } else if (m_verticalTileMode == QQuickBorderImage::Round) { - yChunkCount = qCeil(ySize / yTexSize); - qreal fullHeight = yChunkCount * yTexSize; - yChunkSize = yTexSize * ySize / fullHeight; - } else { - yChunkCount = 1; - yChunkSize = ySize; - } - - int xTotalChunkCount = xChunkCount + 2; - int yTotalChunkCount = yChunkCount + 2; - - int totalChunkCount = xTotalChunkCount * yTotalChunkCount; - int vertexCount = totalChunkCount * 4; - int indexCount = totalChunkCount * 6; - - if (vertexCount != m_geometry.vertexCount() || indexCount != m_geometry.indexCount()) - m_geometry.allocate(vertexCount, indexCount); - - QSGGeometry::TexturedPoint2D *v = m_geometry.vertexDataAsTexturedPoint2D(); - - - // Fill in the vertices.. The loop below is pretty much an exact replica - // of the one inside fillRow. - float yTexChunk1 = m_innerRect.top() / th; - float yTexChunk2 = m_innerRect.bottom() / th; - - fillRow(v, 0, 0, xChunkCount, xChunkSize, textureSubRect, textureSize); - fillRow(v, m_innerRect.y(), yTexChunk1, xChunkCount, xChunkSize, textureSubRect, textureSize); - - for (int yc=0; yc<yChunkCount; ++yc) { - float yy = m_innerRect.y() + yChunkSize * yc; - fillRow(v, yy, yTexChunk1, xChunkCount, xChunkSize, textureSubRect, textureSize); - - // Special case the last one - if (yc == yChunkCount - 1) { - float t = m_verticalTileMode == QQuickBorderImage::Repeat - ? yTexChunk1 + (yTexChunk2 - yTexChunk1) * (m_targetRect.height() - bottomBorder - yy) / yChunkSize - : yTexChunk2; - fillRow(v, m_targetRect.height() - bottomBorder, t, xChunkCount, xChunkSize, textureSubRect, textureSize); - } else { - fillRow(v, yy + yChunkSize, yTexChunk2, xChunkCount, xChunkSize, textureSubRect, textureSize); - } - } - - fillRow(v, m_targetRect.height() - bottomBorder, yTexChunk2, xChunkCount, xChunkSize, textureSubRect, textureSize); - fillRow(v, m_targetRect.height(), 1, xChunkCount, xChunkSize, textureSubRect, textureSize); - - if (m_mirror) { - v = m_geometry.vertexDataAsTexturedPoint2D(); - for (int i=0; i<m_geometry.vertexCount(); ++i) { - v->x = m_targetRect.width() - v->x; - ++v; - } - } - -// v = m_geometry.vertexDataAsTexturedPoint2D(); -// for (int i=0; i<m_geometry.vertexCount(); ++i) { -// printf("Vertex: %d: (%.3f, %.3f) - (%.3f, %.3f)\n", -// i, -// v->x, v->y, v->tx, v->ty); -// ++v; -// } - - quint16 *i = m_geometry.indexDataAsUShort(); - int row = xTotalChunkCount * 2; - for (int r=0; r<yTotalChunkCount; ++r) { - int offset = r * row * 2; - for (int c=0; c<xTotalChunkCount; ++c) { - *i++ = offset + c * 2; - *i++ = offset + c * 2 + 1; - *i++ = offset + c * 2 + row; - *i++ = offset + c * 2 + 1; - *i++ = offset + c * 2 + row + 1; - *i++ = offset + c * 2 + row; - } - } - -// i = m_geometry.indexDataAsUShort(); -// for (int idx=0; idx<m_geometry.indexCount(); idx+=6) { -// printf("%2d: ", idx / 6); -// for (int s=0; s<6; ++s) -// printf(" %d", i[idx + s]); -// printf("\n"); -// } - - markDirty(QSGNode::DirtyGeometry); -} - -void QQuickNinePatchNode::fillRow(QSGGeometry::TexturedPoint2D *&v, float y, float ty, int xChunkCount, float xChunkSize, - const QRectF &tsr, // texture sub rect, for atlasses - const QSize &ts) // texture size in pixels -{ - ty = tsr.y() + ty * tsr.height(); - - float tw = ts.width(); - float rightBorder = tw - m_innerRect.right(); - float xTexChunk1 = tsr.left() + tsr.width() * m_innerRect.left() / tw; - float xTexChunk2 = tsr.left() + tsr.width() * m_innerRect.right() / tw; - - v++->set(0, y, tsr.left(), ty); - v++->set(m_innerRect.x(), y, xTexChunk1, ty); - - for (int xc=0; xc<xChunkCount; ++xc) { - float xx = m_innerRect.x() + xChunkSize * xc; - v++->set(xx, y, xTexChunk1, ty); - - // Special case the last one - if (xc == xChunkCount - 1) { - float t = m_horizontalTileMode == QQuickBorderImage::Repeat - ? xTexChunk1 + (xTexChunk2 - xTexChunk1) * (m_targetRect.width() - rightBorder - xx) / xChunkSize - : xTexChunk2; - v->set(m_targetRect.width() - rightBorder, y, t, ty); - } else { - v->set(xx + xChunkSize, y, xTexChunk2, ty); - } - ++v; - } - - v++->set(m_targetRect.width() - rightBorder, y, xTexChunk2, ty); - v++->set(m_targetRect.width(), y, tsr.right(), ty); -} diff --git a/src/quick/items/qquickninepatchnode_p.h b/src/quick/items/qquickninepatchnode_p.h deleted file mode 100644 index c2761f74e5..0000000000 --- a/src/quick/items/qquickninepatchnode_p.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKNINEPATCHNODE_H -#define QQUICKNINEPATCHNODE_H - -#include <QtQuick/qsgnode.h> -#include <QtQuick/qsgtexturematerial.h> -#include "qquickborderimage_p.h" - -class TextureReference; - -class QQuickNinePatchNode : public QSGGeometryNode -{ -public: - QQuickNinePatchNode(); - - void setTexture(QSGTexture *texture); - QSGTexture *texture() const; - - void setRect(const QRectF &rect); - QRectF rect() const { return m_targetRect; } - - void setInnerRect(const QRectF &rect); - QRectF innerRect() const { return m_innerRect; } - - void setFiltering(QSGTexture::Filtering filtering); - QSGTexture::Filtering filtering() const; - - void setHorzontalTileMode(QQuickBorderImage::TileMode mode); - QQuickBorderImage::TileMode horizontalTileMode() const { - return (QQuickBorderImage::TileMode) m_horizontalTileMode; - } - - void setVerticalTileMode(QQuickBorderImage::TileMode mode); - QQuickBorderImage::TileMode verticalTileMode() const { - return (QQuickBorderImage::TileMode) m_verticalTileMode; - } - - void setMirror(bool m); - bool mirror() const { return m_mirror; } - - void update(); - -private: - void fillRow(QSGGeometry::TexturedPoint2D *&v, float y, float ty, int xChunkCount, float xChunkSize, const QRectF &tsr, const QSize &ts); - QRectF m_targetRect; - QRectF m_innerRect; - QSGOpaqueTextureMaterial m_material; - QSGTextureMaterial m_materialO; - QSGGeometry m_geometry; - - uint m_horizontalTileMode : 2; - uint m_verticalTileMode : 2; - - uint m_dirtyGeometry : 1; - uint m_mirror : 1; -}; - -#endif // QQUICKNINEPATCHNODE_H diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp index 608fd94ac1..ec300e87b0 100644 --- a/src/quick/items/qquickrectangle.cpp +++ b/src/quick/items/qquickrectangle.cpp @@ -502,6 +502,7 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData } rectangle->setRadius(d->radius); + rectangle->setAntialiasing(d->antialiasing); QGradientStops stops; if (d->gradient) { diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp index 3f36c86d0c..20b919018e 100644 --- a/src/quick/items/qquickshadereffectsource.cpp +++ b/src/quick/items/qquickshadereffectsource.cpp @@ -1006,7 +1006,7 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint node->setHorizontalWrapMode(hWrap); node->setVerticalWrapMode(vWrap); node->setTargetRect(QRectF(0, 0, width(), height())); - node->setSourceRect(QRectF(0, 0, 1, 1)); + node->setInnerTargetRect(QRectF(0, 0, width(), height())); node->update(); return node; diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h index 450dd2866f..a421bb5dcf 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.h +++ b/src/quick/scenegraph/coreapi/qsgmaterial.h @@ -116,8 +116,9 @@ class Q_QUICK_EXPORT QSGMaterial public: enum Flag { Blending = 0x0001, - RequiresDeterminant = 0x0002, - RequiresFullMatrix = 0x0004 | RequiresDeterminant + RequiresDeterminant = 0x0002, // Allow precalculated translation and 2D rotation + RequiresFullMatrixExceptTranslate = 0x0004 | RequiresDeterminant, // Allow precalculated translation + RequiresFullMatrix = 0x0008 | RequiresFullMatrixExceptTranslate }; Q_DECLARE_FLAGS(Flags, Flag) diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index c1b2afc8fa..76e2fea455 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -69,7 +69,6 @@ class TextureReference; class QSGDistanceFieldGlyphCacheManager; class QSGDistanceFieldGlyphNode; -// TODO: Rename from XInterface to AbstractX. class Q_QUICK_PRIVATE_EXPORT QSGRectangleNode : public QSGGeometryNode { public: @@ -79,6 +78,7 @@ public: virtual void setPenWidth(qreal width) = 0; virtual void setGradientStops(const QGradientStops &stops) = 0; virtual void setRadius(qreal radius) = 0; + virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing) } virtual void setAligned(bool aligned) = 0; virtual void update() = 0; @@ -89,9 +89,15 @@ class Q_QUICK_PRIVATE_EXPORT QSGImageNode : public QSGGeometryNode { public: virtual void setTargetRect(const QRectF &rect) = 0; - virtual void setSourceRect(const QRectF &rect) = 0; + virtual void setInnerTargetRect(const QRectF &rect) = 0; + virtual void setInnerSourceRect(const QRectF &rect) = 0; + // The sub-source rect's width and height specify the number of times the inner source rect + // is repeated inside the inner target rect. The x and y specify which (normalized) location + // in the inner source rect maps to the upper-left corner of the inner target rect. + virtual void setSubSourceRect(const QRectF &rect) = 0; virtual void setTexture(QSGTexture *texture) = 0; - + virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing) } + virtual void setMirror(bool mirror) = 0; virtual void setMipmapFiltering(QSGTexture::Filtering filtering) = 0; virtual void setFiltering(QSGTexture::Filtering filtering) = 0; virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) = 0; diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index dec9ea9f7d..47cc6670f2 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -378,7 +378,6 @@ QSurfaceFormat QSGContext::defaultSurfaceFormat() const QSurfaceFormat format; format.setDepthBufferSize(24); format.setStencilBufferSize(8); - format.setSamples(16); return format; } diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp index e6855c4c78..2ccf9d9b72 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode.cpp +++ b/src/quick/scenegraph/qsgdefaultimagenode.cpp @@ -41,16 +41,163 @@ #include "qsgdefaultimagenode_p.h" -#include <QtQuick/qsgtextureprovider.h> - #include <QtCore/qvarlengtharray.h> #include <QtCore/qmath.h> #include <QtGui/qopenglfunctions.h> +#include <qsgtexturematerial.h> +#include <private/qsgtexturematerial_p.h> +#include <qsgmaterial.h> + QT_BEGIN_NAMESPACE +namespace +{ + struct SmoothVertex + { + float x, y, u, v; + float dx, dy, du, dv; + }; + + const QSGGeometry::AttributeSet &smoothAttributeSet() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), + QSGGeometry::Attribute::create(1, 2, GL_FLOAT, false), + QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false), + QSGGeometry::Attribute::create(3, 2, GL_FLOAT, false) + }; + static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data }; + return attrs; + } +} + +class SmoothTextureMaterialShader : public QSGTextureMaterialShader +{ +public: + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + +protected: + virtual void initialize(); + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + + int m_pixelSizeLoc; +}; + + +SmoothTextureMaterial::SmoothTextureMaterial() +{ + setFlag(RequiresFullMatrixExceptTranslate, true); + setFlag(Blending, true); +} + +void SmoothTextureMaterial::setTexture(QSGTexture *texture) +{ + m_texture = texture; +} + +QSGMaterialType *SmoothTextureMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *SmoothTextureMaterial::createShader() const +{ + return new SmoothTextureMaterialShader; +} + +void SmoothTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + if (oldEffect == 0) { + // The viewport is constant, so set the pixel size uniform only once. + QRect r = state.viewportRect(); + program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); + } + QSGTextureMaterialShader::updateState(state, newEffect, oldEffect); +} + +char const *const *SmoothTextureMaterialShader::attributeNames() const +{ + static char const *const attributes[] = { + "vertex", + "multiTexCoord", + "vertexOffset", + "texCoord", + 0 + }; + return attributes; +} + +void SmoothTextureMaterialShader::initialize() +{ + m_pixelSizeLoc = program()->uniformLocation("pixelSize"); + QSGTextureMaterialShader::initialize(); +} + +const char *SmoothTextureMaterialShader::vertexShader() const +{ + return + "uniform highp vec2 pixelSize; \n" + "uniform highp mat4 qt_Matrix; \n" + "uniform lowp float opacity; \n" + "attribute highp vec4 vertex; \n" + "attribute highp vec2 multiTexCoord; \n" + "attribute highp vec2 vertexOffset; \n" + "attribute highp vec2 texCoordOffset; \n" + "varying highp vec2 texCoord; \n" + "varying lowp float vertexOpacity; \n" + "void main() { \n" + " highp vec4 pos = qt_Matrix * vertex; \n" + " gl_Position = pos; \n" + " texCoord = multiTexCoord; \n" + + " if (vertexOffset.x != 0.) { \n" + " highp vec4 delta = qt_Matrix[0] * vertexOffset.x; \n" + " highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; \n" + " highp vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize); \n" + " dir -= ndir * delta.w * pos.w; \n" + " highp float scale = min(1., dot(dir, ndir * pos.w * pos.w) / dot(dir, dir)); \n" + " if (scale < 0.) scale = 1.; \n" + " gl_Position += scale * delta; \n" + " texCoord.x += scale * texCoordOffset.x; \n" + " } \n" + + " if (vertexOffset.y != 0.) { \n" + " highp vec4 delta = qt_Matrix[1] * vertexOffset.y; \n" + " highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; \n" + " highp vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize); \n" + " dir -= ndir * delta.w * pos.w; \n" + " highp float scale = min(1., dot(dir, ndir * pos.w * pos.w) / dot(dir, dir)); \n" + " if (scale < 0.) scale = 1.; \n" + " gl_Position += scale * delta; \n" + " texCoord.y += scale * texCoordOffset.y; \n" + " } \n" + + " bool onEdge = any(notEqual(vertexOffset, vec2(0.))); \n" + " bool outerEdge = all(equal(texCoordOffset, vec2(0.))); \n" + " vertexOpacity = onEdge && outerEdge ? 0. : opacity; \n" + "}"; +} + +const char *SmoothTextureMaterialShader::fragmentShader() const +{ + return + "uniform sampler2D qt_Texture; \n" + "varying highp vec2 texCoord; \n" + "varying lowp float vertexOpacity; \n" + "void main() { \n" + " gl_FragColor = texture2D(qt_Texture, texCoord) * vertexOpacity; \n" + "}"; +} + QSGDefaultImageNode::QSGDefaultImageNode() - : m_sourceRect(0, 0, 1, 1) + : m_innerSourceRect(0, 0, 1, 1) + , m_subSourceRect(0, 0, 1, 1) + , m_antialiasing(false) + , m_mirror(false) , m_dirtyGeometry(false) , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) { @@ -71,14 +218,29 @@ void QSGDefaultImageNode::setTargetRect(const QRectF &rect) m_dirtyGeometry = true; } -void QSGDefaultImageNode::setSourceRect(const QRectF &rect) +void QSGDefaultImageNode::setInnerTargetRect(const QRectF &rect) +{ + if (rect == m_innerTargetRect) + return; + m_innerTargetRect = rect; + m_dirtyGeometry = true; +} + +void QSGDefaultImageNode::setInnerSourceRect(const QRectF &rect) { - if (rect == m_sourceRect) + if (rect == m_innerSourceRect) return; - m_sourceRect = rect; + m_innerSourceRect = rect; m_dirtyGeometry = true; } +void QSGDefaultImageNode::setSubSourceRect(const QRectF &rect) +{ + if (rect == m_subSourceRect) + return; + m_subSourceRect = rect; + m_dirtyGeometry = true; +} void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering) { @@ -87,6 +249,7 @@ void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering) m_material.setFiltering(filtering); m_materialO.setFiltering(filtering); + m_smoothMaterial.setFiltering(filtering); markDirty(DirtyMaterial); } @@ -98,6 +261,7 @@ void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) m_material.setMipmapFiltering(filtering); m_materialO.setMipmapFiltering(filtering); + m_smoothMaterial.setMipmapFiltering(filtering); markDirty(DirtyMaterial); } @@ -108,6 +272,7 @@ void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) m_material.setVerticalWrapMode(wrapMode); m_materialO.setVerticalWrapMode(wrapMode); + m_smoothMaterial.setVerticalWrapMode(wrapMode); markDirty(DirtyMaterial); } @@ -118,6 +283,7 @@ void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) m_material.setHorizontalWrapMode(wrapMode); m_materialO.setHorizontalWrapMode(wrapMode); + m_smoothMaterial.setHorizontalWrapMode(wrapMode); markDirty(DirtyMaterial); } @@ -129,6 +295,7 @@ void QSGDefaultImageNode::setTexture(QSGTexture *texture) m_material.setTexture(texture); m_materialO.setTexture(texture); + m_smoothMaterial.setTexture(texture); // Texture cleanup // if (!texture.isNull()) // m_material.setBlending(texture->hasAlphaChannel()); @@ -138,6 +305,34 @@ void QSGDefaultImageNode::setTexture(QSGTexture *texture) m_dirtyGeometry = true; } +void QSGDefaultImageNode::setAntialiasing(bool antialiasing) +{ + if (antialiasing == m_antialiasing) + return; + m_antialiasing = antialiasing; + if (m_antialiasing) { + setMaterial(&m_smoothMaterial); + setOpaqueMaterial(0); + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + setFlag(OwnsGeometry, true); + } else { + setMaterial(&m_materialO); + setOpaqueMaterial(&m_material); + setGeometry(&m_geometry); + setFlag(OwnsGeometry, false); + } + m_dirtyGeometry = true; +} + +void QSGDefaultImageNode::setMirror(bool mirror) +{ + if (mirror == m_mirror) + return; + m_mirror = mirror; + m_dirtyGeometry = true; +} + + void QSGDefaultImageNode::update() { if (m_dirtyGeometry) @@ -174,116 +369,348 @@ namespace { struct Y { float y, ty; }; } +static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, + quint16 bottomLeft, quint16 bottomRight) +{ + *(*indices)++ = topLeft; + *(*indices)++ = bottomLeft; + *(*indices)++ = bottomRight; + *(*indices)++ = bottomRight; + *(*indices)++ = topRight; + *(*indices)++ = topLeft; +} + void QSGDefaultImageNode::updateGeometry() { + Q_ASSERT(!m_targetRect.isEmpty()); const QSGTexture *t = m_material.texture(); if (!t) { - m_geometry.allocate(4); - m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); - QSGGeometry::updateTexturedRectGeometry(&m_geometry, QRectF(), QRectF()); + QSGGeometry *g = geometry(); + g->allocate(4); + g->setDrawingMode(GL_TRIANGLE_STRIP); + memset(g->vertexData(), 0, g->sizeOfVertex() * 4); } else { - QRectF textureRect = t->normalizedTextureSubRect(); + QRectF sourceRect = t->normalizedTextureSubRect(); + + QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(), + sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(), + m_innerSourceRect.width() * sourceRect.width(), + m_innerSourceRect.height() * sourceRect.height()); + + bool hasMargins = m_targetRect != m_innerTargetRect; - bool isSubRect = textureRect != QRectF(0, 0, 1, 1); - const int ceilRight = qCeil(m_sourceRect.right()); - const int floorLeft = qFloor(m_sourceRect.left()); - const int ceilBottom = qCeil(m_sourceRect.bottom()); - const int floorTop = qFloor(m_sourceRect.top()); - const int hCells = ceilRight - floorLeft; - const int vCells = ceilBottom - floorTop; - bool isRepeating = hCells > 1 || vCells > 1; + int floorLeft = qFloor(m_subSourceRect.left()); + int ceilRight = qCeil(m_subSourceRect.right()); + int floorTop = qFloor(m_subSourceRect.top()); + int ceilBottom = qCeil(m_subSourceRect.bottom()); + int hTiles = ceilRight - floorLeft; + int vTiles = ceilBottom - floorTop; + + bool hasTiles = hTiles != 1 || vTiles != 1; + bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1); #ifdef QT_OPENGL_ES_2 QOpenGLContext *ctx = QOpenGLContext::currentContext(); bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); - QSize size = t->textureSize(); bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); - - if (isRepeating && (isSubRect || (isNpot && !npotSupported))) { + bool wrapSupported = npotSupported || !isNpot; #else - if (isRepeating && isSubRect) { + bool wrapSupported = true; #endif - m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6); - m_geometry.setDrawingMode(GL_TRIANGLES); + + // An image can be rendered as a single quad if: + // - There are no margins, and either: + // - the image isn't repeated + // - the source rectangle fills the entire texture so that texture wrapping can be used, + // and NPOT is supported + if (!hasMargins && (!hasTiles || (fullTexture && wrapSupported))) { + QRectF sr; + if (!fullTexture) { + sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(), + innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(), + m_subSourceRect.width() * innerSourceRect.width(), + m_subSourceRect.height() * innerSourceRect.height()); + } else { + sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop, + m_subSourceRect.width(), m_subSourceRect.height()); + } + if (m_mirror) { + qreal oldLeft = sr.left(); + sr.setLeft(sr.right()); + sr.setRight(oldLeft); + } + + if (m_antialiasing) { + QSGGeometry *g = geometry(); + Q_ASSERT(g != &m_geometry); + g->allocate(8, 14); + g->setDrawingMode(GL_TRIANGLE_STRIP); + SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); + float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) + ? m_targetRect.width() : m_targetRect.height()) * 0.5f; + float sx = float(sr.width() / m_targetRect.width()); + float sy = float(sr.height() / m_targetRect.height()); + for (int d = -1; d <= 1; d += 2) { + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 2; ++i, ++vertices) { + vertices->x = m_targetRect.x() + i * m_targetRect.width(); + vertices->y = m_targetRect.y() + j * m_targetRect.height(); + vertices->u = sr.x() + i * sr.width(); + vertices->v = sr.y() + j * sr.height(); + vertices->dx = (i == 0 ? delta : -delta) * d; + vertices->dy = (j == 0 ? delta : -delta) * d; + vertices->du = (d < 0 ? 0 : vertices->dx * sx); + vertices->dv = (d < 0 ? 0 : vertices->dy * sy); + } + } + } + Q_ASSERT(vertices - g->vertexCount() == g->vertexData()); + static const quint16 indices[] = { + 0, 4, 1, 5, 3, 7, 2, 6, 0, 4, + 4, 6, 5, 7 + }; + Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices)); + memcpy(g->indexDataAsUShort(), indices, sizeof(indices)); + } else { + m_geometry.allocate(4); + m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); + } + } else { + int hCells = hTiles; + int vCells = vTiles; + if (m_innerTargetRect.width() == 0) + hCells = 0; + if (m_innerTargetRect.left() != m_targetRect.left()) + ++hCells; + if (m_innerTargetRect.right() != m_targetRect.right()) + ++hCells; + if (m_innerTargetRect.height() == 0) + vCells = 0; + if (m_innerTargetRect.top() != m_targetRect.top()) + ++vCells; + if (m_innerTargetRect.bottom() != m_targetRect.bottom()) + ++vCells; QVarLengthArray<X, 32> xData(2 * hCells); QVarLengthArray<Y, 32> yData(2 * vCells); X *xs = xData.data(); Y *ys = yData.data(); - - xs->x = m_targetRect.left(); - xs->tx = textureRect.x() + (m_sourceRect.left() - floorLeft) * textureRect.width(); - ++xs; - ys->y = m_targetRect.top(); - ys->ty = textureRect.y() + (m_sourceRect.top() - floorTop) * textureRect.height(); - ++ys; - - float a, b; - b = m_targetRect.width() / m_sourceRect.width(); - a = m_targetRect.x() - m_sourceRect.x() * b; - - float tex_x1 = textureRect.x(); - float tex_x2 = textureRect.right(); - float tex_y1 = textureRect.y(); - float tex_y2 = textureRect.bottom(); - for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { - xs[0].x = xs[1].x = a + b * i; - xs[0].tx = tex_x2; - xs[1].tx = tex_x1; + + if (m_innerTargetRect.left() != m_targetRect.left()) { + xs[0].x = m_targetRect.left(); + xs[0].tx = sourceRect.left(); + xs[1].x = m_innerTargetRect.left(); + xs[1].tx = innerSourceRect.left(); xs += 2; } - b = m_targetRect.height() / m_sourceRect.height(); - a = m_targetRect.y() - m_sourceRect.y() * b; - for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { - ys[0].y = ys[1].y = a + b * i; - ys[0].ty = tex_y2; - ys[1].ty = tex_y1; - ys += 2; + if (m_innerTargetRect.width() != 0) { + xs[0].x = m_innerTargetRect.left(); + xs[0].tx = innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(); + ++xs; + float b = m_innerTargetRect.width() / m_subSourceRect.width(); + float a = m_innerTargetRect.x() - m_subSourceRect.x() * b; + for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { + xs[0].x = xs[1].x = a + b * i; + xs[0].tx = innerSourceRect.right(); + xs[1].tx = innerSourceRect.left(); + xs += 2; + } + xs[0].x = m_innerTargetRect.right(); + xs[0].tx = innerSourceRect.x() + (m_subSourceRect.right() - ceilRight + 1) * innerSourceRect.width(); + ++xs; + } + if (m_innerTargetRect.right() != m_targetRect.right()) { + xs[0].x = m_innerTargetRect.right(); + xs[0].tx = innerSourceRect.right(); + xs[1].x = m_targetRect.right(); + xs[1].tx = sourceRect.right(); + xs += 2; + } + Q_ASSERT(xs == xData.data() + xData.size()); + if (m_mirror) { + float leftPlusRight = m_targetRect.left() + m_targetRect.right(); + int count = xData.size(); + xs = xData.data(); + for (int i = 0; i < count >> 1; ++i) + qSwap(xs[i], xs[count - 1 - i]); + for (int i = 0; i < count; ++i) + xs[i].x = leftPlusRight - xs[i].x; } - xs->x = m_targetRect.right(); - xs->tx = textureRect.x() + (m_sourceRect.right() - ceilRight + 1) * textureRect.width(); + if (m_innerTargetRect.top() != m_targetRect.top()) { + ys[0].y = m_targetRect.top(); + ys[0].ty = sourceRect.top(); + ys[1].y = m_innerTargetRect.top(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + if (m_innerTargetRect.height() != 0) { + ys[0].y = m_innerTargetRect.top(); + ys[0].ty = innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(); + ++ys; + float b = m_innerTargetRect.height() / m_subSourceRect.height(); + float a = m_innerTargetRect.y() - m_subSourceRect.y() * b; + for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { + ys[0].y = ys[1].y = a + b * i; + ys[0].ty = innerSourceRect.bottom(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + ys[0].y = m_innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.y() + (m_subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height(); + ++ys; + } + if (m_innerTargetRect.bottom() != m_targetRect.bottom()) { + ys[0].y = m_innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.bottom(); + ys[1].y = m_targetRect.bottom(); + ys[1].ty = sourceRect.bottom(); + ys += 2; + } + Q_ASSERT(ys == yData.data() + yData.size()); + + if (m_antialiasing) { + QSGGeometry *g = geometry(); + Q_ASSERT(g != &m_geometry); + + g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, + hCells * vCells * 6 + (hCells + vCells) * 12); + g->setDrawingMode(GL_TRIANGLES); + SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); + memset(vertices, 0, g->vertexCount() * g->sizeOfVertex()); + quint16 *indices = g->indexDataAsUShort(); + + // The deltas are how much the fuzziness can reach into the image. + // Only the border vertices are moved by the vertex shader, so the fuzziness + // can't reach further into the image than the closest interior vertices. + float leftDx = xData.at(1).x - xData.at(0).x; + float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x; + float topDy = yData.at(1).y - yData.at(0).y; + float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y; + + float leftDu = xData.at(1).tx - xData.at(0).tx; + float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx; + float topDv = yData.at(1).ty - yData.at(0).ty; + float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty; + + if (hCells == 1) { + leftDx = rightDx *= 0.5f; + leftDu = rightDu *= 0.5f; + } + if (vCells == 1) { + topDy = bottomDy *= 0.5f; + topDv = bottomDv *= 0.5f; + } - ys->y = m_targetRect.bottom(); - ys->ty = textureRect.y() + (m_sourceRect.bottom() - ceilBottom + 1) * textureRect.height(); + // This delta is how much the fuzziness can reach out from the image. + float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) + ? m_targetRect.width() : m_targetRect.height()) * 0.5f; + + quint16 index = 0; + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + bool isTop = j == 0; + bool isBottom = j == vCells - 1; + for (int i = 0; i < hCells; ++i, xs += 2) { + bool isLeft = i == 0; + bool isRight = i == hCells - 1; + + SmoothVertex *v = vertices + index; + + quint16 topLeft = index; + for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 topRight = index; + for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 bottomLeft = index; + for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + quint16 bottomRight = index; + for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); + + if (isTop) { + vertices[topLeft].dy = vertices[topRight].dy = topDy; + vertices[topLeft].dv = vertices[topRight].dv = topDv; + vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta; + appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight); + } + + if (isBottom) { + vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy; + vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv; + vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta; + appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); + } + + if (isLeft) { + vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx; + vertices[topLeft].du = vertices[bottomLeft].du = leftDu; + vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta; + appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); + } + + if (isRight) { + vertices[topRight].dx = vertices[bottomRight].dx = -rightDx; + vertices[topRight].du = vertices[bottomRight].du = -rightDu; + vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta; + appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1); + } + } + } - QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D(); - ys = yData.data(); - for (int j = 0; j < vCells; ++j, ys += 2) { - xs = xData.data(); - for (int i = 0; i < hCells; ++i, xs += 2) { - vertices[0].x = vertices[2].x = xs[0].x; - vertices[0].tx = vertices[2].tx = xs[0].tx; - vertices[1].x = vertices[3].x = xs[1].x; - vertices[1].tx = vertices[3].tx = xs[1].tx; - - vertices[0].y = vertices[1].y = ys[0].y; - vertices[0].ty = vertices[1].ty = ys[0].ty; - vertices[2].y = vertices[3].y = ys[1].y; - vertices[2].ty = vertices[3].ty = ys[1].ty; - - vertices += 4; + Q_ASSERT(index == g->vertexCount()); + Q_ASSERT(indices - g->indexCount() == g->indexData()); + } else { + m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6); + m_geometry.setDrawingMode(GL_TRIANGLES); + QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D(); + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + for (int i = 0; i < hCells; ++i, xs += 2) { + vertices[0].x = vertices[2].x = xs[0].x; + vertices[0].tx = vertices[2].tx = xs[0].tx; + vertices[1].x = vertices[3].x = xs[1].x; + vertices[1].tx = vertices[3].tx = xs[1].tx; + + vertices[0].y = vertices[1].y = ys[0].y; + vertices[0].ty = vertices[1].ty = ys[0].ty; + vertices[2].y = vertices[3].y = ys[1].y; + vertices[2].ty = vertices[3].ty = ys[1].ty; + + vertices += 4; + } } - } - quint16 *indices = m_geometry.indexDataAsUShort(); - for (int i = 0; i < 4 * vCells * hCells; i += 4) { - *indices++ = i; - *indices++ = i + 2; - *indices++ = i + 3; - *indices++ = i + 3; - *indices++ = i + 1; - *indices++ = i; + quint16 *indices = m_geometry.indexDataAsUShort(); + for (int i = 0; i < 4 * vCells * hCells; i += 4) + appendQuad(&indices, i, i + 1, i + 2, i + 3); } - } else { - QRectF sr(textureRect.x() + m_sourceRect.x() * textureRect.width(), - textureRect.y() + m_sourceRect.y() * textureRect.height(), - m_sourceRect.width() * textureRect.width(), - m_sourceRect.height() * textureRect.height()); - - m_geometry.allocate(4); - m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); - QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); } } markDirty(DirtyGeometry); diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h index 9062aff2c2..87b3f7760b 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode_p.h +++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h @@ -44,20 +44,35 @@ #define DEFAULT_PIXMAPNODE_H #include <private/qsgadaptationlayer_p.h> - #include <QtQuick/qsgtexturematerial.h> QT_BEGIN_HEADER QT_BEGIN_NAMESPACE +class SmoothTextureMaterial : public QSGTextureMaterial +{ +public: + SmoothTextureMaterial(); + + void setTexture(QSGTexture *texture); + +protected: + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; +}; + class QSGDefaultImageNode : public QSGImageNode { public: QSGDefaultImageNode(); virtual void setTargetRect(const QRectF &rect); - virtual void setSourceRect(const QRectF &rect); + virtual void setInnerTargetRect(const QRectF &rect); + virtual void setInnerSourceRect(const QRectF &rect); + virtual void setSubSourceRect(const QRectF &rect); virtual void setTexture(QSGTexture *t); + virtual void setAntialiasing(bool antialiasing); + virtual void setMirror(bool mirror); virtual void update(); virtual void setMipmapFiltering(QSGTexture::Filtering filtering); @@ -71,11 +86,16 @@ private: void updateGeometry(); QRectF m_targetRect; - QRectF m_sourceRect; + QRectF m_innerTargetRect; + QRectF m_innerSourceRect; + QRectF m_subSourceRect; QSGOpaqueTextureMaterial m_material; QSGTextureMaterial m_materialO; + SmoothTextureMaterial m_smoothMaterial; + uint m_antialiasing : 1; + uint m_mirror : 1; uint m_dirtyGeometry : 1; QSGGeometry m_geometry; diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp index 18cac36550..b204fff2ef 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp +++ b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp @@ -54,45 +54,195 @@ QT_BEGIN_NAMESPACE +namespace +{ + struct Color4ub + { + unsigned char r, g, b, a; + }; + + Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; } + Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; } + + inline Color4ub colorToColor4ub(const QColor &c) + { + Color4ub color = { uchar(c.redF() * c.alphaF() * 255), + uchar(c.greenF() * c.alphaF() * 255), + uchar(c.blueF() * c.alphaF() * 255), + uchar(c.alphaF() * 255) + }; + return color; + } + + // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience. + struct Vertex + { + float x, y; + Color4ub color; + void set(float nx, float ny, Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } + }; + + struct SmoothVertex : public Vertex + { + float dx, dy; + void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy) + { + Vertex::set(nx, ny, ncolor); + dx = ndx; dy = ndy; + } + }; + + const QSGGeometry::AttributeSet &smoothAttributeSet() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), + QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE, false), + QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false) + }; + static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data }; + return attrs; + } +} + +class SmoothColorMaterialShader : public QSGMaterialShader +{ +public: + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + +private: + virtual void initialize(); + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + + int m_matrixLoc; + int m_opacityLoc; + int m_pixelSizeLoc; +}; + +void SmoothColorMaterialShader::updateState(const RenderState &state, QSGMaterial *, QSGMaterial *oldEffect) +{ + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + if (oldEffect == 0) { + // The viewport is constant, so set the pixel size uniform only once. + QRect r = state.viewportRect(); + program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); + } +} + +char const *const *SmoothColorMaterialShader::attributeNames() const +{ + static char const *const attributes[] = { + "vertex", + "vertexColor", + "vertexOffset", + 0 + }; + return attributes; +} + +void SmoothColorMaterialShader::initialize() +{ + m_matrixLoc = program()->uniformLocation("matrix"); + m_opacityLoc = program()->uniformLocation("opacity"); + m_pixelSizeLoc = program()->uniformLocation("pixelSize"); +} + +const char *SmoothColorMaterialShader::vertexShader() const +{ + return + "uniform highp vec2 pixelSize; \n" + "uniform highp mat4 matrix; \n" + "uniform lowp float opacity; \n" + "attribute highp vec4 vertex; \n" + "attribute lowp vec4 vertexColor; \n" + "attribute highp vec2 vertexOffset; \n" + "varying lowp vec4 color; \n" + "void main() { \n" + " highp vec4 pos = matrix * vertex; \n" + " gl_Position = pos; \n" + + " if (vertexOffset.x != 0.) { \n" + " highp vec4 delta = matrix[0] * vertexOffset.x; \n" + " highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; \n" + " highp vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize); \n" + " dir -= ndir * delta.w * pos.w; \n" + " highp float scale = min(1., dot(dir, ndir * pos.w * pos.w) / dot(dir, dir)); \n" + " if (scale < 0.) scale = 1.; \n" + " gl_Position += scale * delta; \n" + " } \n" + + " if (vertexOffset.y != 0.) { \n" + " highp vec4 delta = matrix[1] * vertexOffset.y; \n" + " highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; \n" + " highp vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize); \n" + " dir -= ndir * delta.w * pos.w; \n" + " highp float scale = min(1., dot(dir, ndir * pos.w * pos.w) / dot(dir, dir)); \n" + " if (scale < 0.) scale = 1.; \n" + " gl_Position += scale * delta; \n" + " } \n" + + " color = vertexColor * opacity; \n" + "}"; +} + +const char *SmoothColorMaterialShader::fragmentShader() const +{ + return + "varying lowp vec4 color; \n" + "void main() { \n" + " gl_FragColor = color; \n" + "}"; +} + +SmoothColorMaterial::SmoothColorMaterial() +{ + setFlag(RequiresFullMatrixExceptTranslate, true); + setFlag(Blending, true); +} + +int SmoothColorMaterial::compare(const QSGMaterial *) const +{ + return 0; +} + +QSGMaterialType *SmoothColorMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *SmoothColorMaterial::createShader() const +{ + return new SmoothColorMaterialShader; +} + + QSGDefaultRectangleNode::QSGDefaultRectangleNode() - : m_border(0) - , m_radius(0) + : m_radius(0) , m_pen_width(0) , m_aligned(true) + , m_antialiasing(false) , m_gradient_is_opaque(true) , m_dirty_geometry(false) - , m_default_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) + , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0) { - setGeometry(&m_default_geometry); - setMaterial(&m_fill_material); - m_border_material.setColor(QColor(0, 0, 0)); - - m_material_type = TypeFlat; + setGeometry(&m_geometry); + setMaterial(&m_material); #ifdef QML_RUNTIME_TESTING description = QLatin1String("rectangle"); #endif } -QSGDefaultRectangleNode::~QSGDefaultRectangleNode() -{ - if (m_material_type == TypeVertexGradient) - delete material(); - delete m_border; -} - -QSGGeometryNode *QSGDefaultRectangleNode::border() -{ - if (!m_border) { - m_border = new QSGGeometryNode; - m_border->setMaterial(&m_border_material); - QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 0); - m_border->setGeometry(geometry); - m_border->setFlag(QSGNode::OwnsGeometry); - } - return m_border; -} - void QSGDefaultRectangleNode::setRect(const QRectF &rect) { if (rect == m_rect) @@ -103,22 +253,20 @@ void QSGDefaultRectangleNode::setRect(const QRectF &rect) void QSGDefaultRectangleNode::setColor(const QColor &color) { - if (color == m_fill_material.color()) + if (color == m_color) return; - m_fill_material.setColor(color); - if (m_gradient_stops.isEmpty()) { - Q_ASSERT(m_material_type == TypeFlat); - markDirty(DirtyMaterial); - } + m_color = color; + if (m_gradient_stops.isEmpty()) + m_dirty_geometry = true; } void QSGDefaultRectangleNode::setPenColor(const QColor &color) { - if (color == m_border_material.color()) + if (color == m_border_color) return; - m_border_material.setColor(color); - if (m_border) - m_border->markDirty(DirtyMaterial); + m_border_color = color; + if (m_pen_width > 0) + m_dirty_geometry = true; } void QSGDefaultRectangleNode::setPenWidth(qreal width) @@ -126,10 +274,6 @@ void QSGDefaultRectangleNode::setPenWidth(qreal width) if (width == m_pen_width) return; m_pen_width = width; - if (m_pen_width <= 0 && m_border && m_border->parent()) - removeChildNode(m_border); - else if (m_pen_width > 0 && !border()->parent()) - appendChildNode(m_border); m_dirty_geometry = true; } @@ -144,30 +288,6 @@ void QSGDefaultRectangleNode::setGradientStops(const QGradientStops &stops) m_gradient_is_opaque = true; for (int i = 0; i < stops.size(); ++i) m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff; - - if (stops.isEmpty()) { - // No gradient specified, use flat color. - if (m_material_type != TypeFlat) { - delete material(); - - setMaterial(&m_fill_material); - m_material_type = TypeFlat; - - setGeometry(&m_default_geometry); - setFlag(OwnsGeometry, false); - } - } else { - if (m_material_type == TypeFlat) { - QSGVertexColorMaterial *material = new QSGVertexColorMaterial; - setMaterial(material); - m_material_type = TypeVertexGradient; - QSGGeometry *g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0); - setGeometry(g); - setFlag(OwnsGeometry); - } - static_cast<QSGVertexColorMaterial *>(material())->setFlag(QSGMaterial::Blending, !m_gradient_is_opaque); - } - m_dirty_geometry = true; } @@ -179,6 +299,23 @@ void QSGDefaultRectangleNode::setRadius(qreal radius) m_dirty_geometry = true; } +void QSGDefaultRectangleNode::setAntialiasing(bool antialiasing) +{ + if (antialiasing == m_antialiasing) + return; + m_antialiasing = antialiasing; + if (m_antialiasing) { + setMaterial(&m_smoothMaterial); + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + setFlag(OwnsGeometry, true); + } else { + setMaterial(&m_material); + setGeometry(&m_geometry); + setFlag(OwnsGeometry, false); + } + m_dirty_geometry = true; +} + void QSGDefaultRectangleNode::setAligned(bool aligned) { if (aligned == m_aligned) @@ -195,73 +332,41 @@ void QSGDefaultRectangleNode::update() } } -struct Color4ub -{ - unsigned char r, g, b, a; -}; - -Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; } -Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; } - -static inline Color4ub colorToColor4ub(const QColor &c) -{ - Color4ub color = { uchar(c.redF() * c.alphaF() * 255), - uchar(c.greenF() * c.alphaF() * 255), - uchar(c.blueF() * c.alphaF() * 255), - uchar(c.alphaF() * 255) - }; - return color; -} - -struct Vertex -{ - QVector2D position; -}; - -struct ColorVertex -{ - QVector2D position; - Color4ub color; -}; - void QSGDefaultRectangleNode::updateGeometry() { - qreal penWidth = m_aligned ? qreal(qRound(m_pen_width)) : m_pen_width; - - // fast path for the simple case... - if ((penWidth == 0 || m_border_material.color().alpha() == 0) - && m_radius == 0 - && m_material_type == TypeFlat) { - QSGGeometry::updateRectGeometry(&m_default_geometry, m_rect); - return; - } - - QSGGeometry *fill = geometry(); - - // Check that the vertex type matches the material. - Q_ASSERT(m_material_type != TypeFlat || fill->sizeOfVertex() == sizeof(Vertex)); - Q_ASSERT(m_material_type != TypeVertexGradient || fill->sizeOfVertex() == sizeof(ColorVertex)); - - QSGGeometry *borderGeometry = 0; - if (m_border) { - borderGeometry = m_border->geometry(); - Q_ASSERT(borderGeometry->sizeOfVertex() == sizeof(Vertex)); - } - - int fillVertexCount = 0; - - // Preallocate arrays for a rectangle with 18 segments per corner and 3 gradient stops. - uchar *fillVertices = 0; - Vertex *borderVertices = 0; - - Color4ub fillColor = colorToColor4ub(m_fill_material.color()); + float penWidth = m_aligned ? float(qRound(m_pen_width)) : float(m_pen_width); + float width = float(m_rect.width()); + float height = float(m_rect.height()); + + QSGGeometry *g = geometry(); + g->setDrawingMode(GL_TRIANGLE_STRIP); + int vertexStride = g->sizeOfVertex(); + + union { + Vertex *vertices; + SmoothVertex *smoothVertices; + }; + + Color4ub fillColor = colorToColor4ub(m_color); + Color4ub borderColor = colorToColor4ub(m_border_color); + Color4ub transparent = { 0, 0, 0, 0 }; const QGradientStops &stops = m_gradient_stops; + int nextGradientStop = 0; + float gradientPos = 0.5f * penWidth / height; + while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) + ++nextGradientStop; + int lastGradientStop = stops.size() - 1; + float lastGradientPos = 1.0f - 0.5f * penWidth / height; + while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) + --lastGradientStop; + int gradientIntersections = (lastGradientStop - nextGradientStop + 1); + if (m_radius > 0) { // Rounded corners. // Radius should never exceeds half of the width or half of the height - qreal radius = qMin(qMin(m_rect.width() * qreal(0.5), m_rect.height() * qreal(0.5)), m_radius); + float radius = qMin(qMin(width, height) * 0.5f, float(m_radius)); QRectF innerRect = m_rect; innerRect.adjust(radius, radius, -radius, -radius); if (m_aligned && (int(penWidth) & 1)) { @@ -270,120 +375,158 @@ void QSGDefaultRectangleNode::updateGeometry() innerRect.moveTop(innerRect.top() + qreal(0.5)); } - qreal innerRadius = radius - penWidth * qreal(0.5); - qreal outerRadius = radius + penWidth * qreal(0.5); + float innerRadius = radius - penWidth * 0.5f; + float outerRadius = radius + penWidth * 0.5f; + float delta = qMin(width, height) * 0.5f; // Number of segments per corner, approximately one per 3 pixels. int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18); /* - --+-__ - | segment - | _+ - --+-__ _- \ - -+ segment - --------+ \ <- gradient line - +-----+ - | | + --+--__ + --+--__--__ + | --__--__ + | seg --__--+ + --+-__ ment _+ \ + --+-__--__ - \ \ + --__--+ se \ \ + + \ g \ \ + \ \ m \ \ + -----------+--+ e \ \ <- gradient line + \ \ nt\ \ + fill +--+----+--+ + | | | | + border + inner AA outer AA (AA = antialiasing) */ - int nextGradientStop = 0; - qreal gradientPos = (radius - innerRadius) / (innerRect.height() + 2 * radius); - while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) - ++nextGradientStop; - int lastGradientStop = stops.size() - 1; - qreal lastGradientPos = (innerRect.height() + radius + innerRadius) / (innerRect.height() + 2 * radius); - while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) - --lastGradientStop; - - int borderVertexHead = 0; - int borderVertexTail = 0; + int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2; + int outerVertexCount = (segments + 1) * 4; + int vertexCount = innerVertexCount; + if (m_antialiasing || penWidth) + vertexCount += innerVertexCount; + if (penWidth) + vertexCount += outerVertexCount; + if (m_antialiasing && penWidth) + vertexCount += outerVertexCount; + + int fillIndexCount = innerVertexCount; + int innerAAIndexCount = innerVertexCount * 2 + 2; + int borderIndexCount = innerVertexCount * 2 + 2; + int outerAAIndexCount = outerVertexCount * 2 + 2; + int indexCount = 0; + int fillHead = 0; + int innerAAHead = 0; + int innerAATail = 0; + int borderHead = 0; + int borderTail = 0; + int outerAAHead = 0; + int outerAATail = 0; + bool hasFill = m_color.rgba() != 0 || !stops.isEmpty(); + if (hasFill) + indexCount += fillIndexCount; + if (m_antialiasing) { + innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; + indexCount += innerAAIndexCount; + } if (penWidth) { - // The reason I add extra vertices where the gradient lines intersect the border is - // to avoid pixel sized gaps between the fill and the border caused by floating point - // inaccuracies. - borderGeometry->allocate((segments + 1) * 2 * 4 + (lastGradientStop - nextGradientStop + 1) * 4 + 2); - borderVertexHead = borderVertexTail = (borderGeometry->vertexCount() >> 1) - 1; - borderVertices = (Vertex *)borderGeometry->vertexData(); + borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; + indexCount += borderIndexCount; + } + if (m_antialiasing && penWidth) { + outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; + indexCount += outerAAIndexCount; } - fill->allocate((segments + 1) * 4 + (lastGradientStop - nextGradientStop + 1) * 2); - fillVertices = (uchar *)fill->vertexData(); + g->allocate(vertexCount, indexCount); + vertices = reinterpret_cast<Vertex *>(g->vertexData()); + memset(vertices, 0, vertexCount * vertexStride); + quint16 *indices = g->indexDataAsUShort(); + quint16 index = 0; - qreal py = 0; // previous inner y-coordinate. - qreal plx = 0; // previous inner left x-coordinate. - qreal prx = 0; // previous inner right x-coordinate. + float py = 0; // previous inner y-coordinate. + float plx = 0; // previous inner left x-coordinate. + float prx = 0; // previous inner right x-coordinate. - qreal angle = qreal(0.5) * M_PI / qreal(segments); - qreal cosStep = qFastCos(angle); - qreal sinStep = qFastSin(angle); + float angle = 0.5f * float(M_PI) / segments; + float cosStep = qFastCos(angle); + float sinStep = qFastSin(angle); for (int part = 0; part < 2; ++part) { - qreal c = 1 - part; - qreal s = part; + float c = 1 - part; + float s = part; for (int i = 0; i <= segments; ++i) { - qreal y, lx, rx; + float y, lx, rx; if (innerRadius > 0) { y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate. lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate. rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate. - gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / (innerRect.height() + 2 * radius); + gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height; } else { y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate. lx = innerRect.left() - innerRadius; // current inner left x-coordinate. rx = innerRect.right() + innerRadius; // current inner right x-coordinate. - gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / (innerRect.height() + 2 * radius); + gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height; } - qreal Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate. - qreal lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate. - qreal rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate. + float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate. + float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate. + float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate. while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { // Insert vertices at gradient stops. - qreal gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * (innerRect.height() + 2 * radius); - Q_ASSERT(fillVertexCount >= 2); - qreal t = (gy - py) / (y - py); - qreal glx = plx * (1 - t) + t * lx; - qreal grx = prx * (1 - t) + t * rx; + float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height; + float t = (gy - py) / (y - py); + float glx = plx * (1 - t) + t * lx; + float grx = prx * (1 - t) + t * rx; - if (penWidth) { - const Vertex &first = borderVertices[borderVertexHead]; - borderVertices[--borderVertexHead].position = QVector2D(glx, gy); - borderVertices[--borderVertexHead] = first; + fillColor = colorToColor4ub(stops.at(nextGradientStop).second); - const Vertex &last = borderVertices[borderVertexTail - 2]; - borderVertices[borderVertexTail++] = last; - borderVertices[borderVertexTail++].position = QVector2D(grx, gy); + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; } - ColorVertex *vertices = (ColorVertex *)fillVertices; - - fillColor = colorToColor4ub(stops.at(nextGradientStop).second); - vertices[fillVertexCount].position = QVector2D(grx, gy); - vertices[fillVertexCount].color = fillColor; - ++fillVertexCount; - vertices[fillVertexCount].position = QVector2D(glx, gy); - vertices[fillVertexCount].color = fillColor; - ++fillVertexCount; + if (penWidth) { + --borderHead; + indices[borderHead] = indices[borderHead + 2]; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail] = indices[borderTail - 2]; + ++borderTail; + } + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + bool lower = stops.at(nextGradientStop).first > 0.5f; + float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); + smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy); + smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy); + if (penWidth) { + smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); + } else { + dy = lower ? delta : -delta; + smoothVertices[index++].set(grx, gy, transparent, delta, dy); + smoothVertices[index++].set(glx, gy, transparent, -delta, dy); + } + } else { + vertices[index++].set(grx, gy, fillColor); + vertices[index++].set(glx, gy, fillColor); + if (penWidth) { + vertices[index++].set(grx, gy, borderColor); + vertices[index++].set(glx, gy, borderColor); + } + } ++nextGradientStop; } - if (penWidth) { - borderVertices[--borderVertexHead].position = QVector2D(lx, y); - borderVertices[--borderVertexHead].position = QVector2D(lX, Y); - borderVertices[borderVertexTail++].position = QVector2D(rX, Y); - borderVertices[borderVertexTail++].position = QVector2D(rx, y); - } - - if (stops.isEmpty()) { - Q_ASSERT(m_material_type == TypeFlat); - Vertex *vertices = (Vertex *)fillVertices; - vertices[fillVertexCount++].position = QVector2D(rx, y); - vertices[fillVertexCount++].position = QVector2D(lx, y); - } else { + if (!stops.isEmpty()) { if (nextGradientStop == 0) { fillColor = colorToColor4ub(stops.at(0).second); } else if (nextGradientStop == stops.size()) { @@ -391,18 +534,61 @@ void QSGDefaultRectangleNode::updateGeometry() } else { const QGradientStop &prev = stops.at(nextGradientStop - 1); const QGradientStop &next = stops.at(nextGradientStop); - qreal t = (gradientPos - prev.first) / (next.first - prev.first); - fillColor = (colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t); + float t = (gradientPos - prev.first) / (next.first - prev.first); + fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; } + } + + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + indices[--borderHead] = index + 4; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail++] = index + 5; + } - ColorVertex *vertices = (ColorVertex *)fillVertices; - vertices[fillVertexCount].position = QVector2D(rx, y); - vertices[fillVertexCount].color = fillColor; - ++fillVertexCount; - vertices[fillVertexCount].position = QVector2D(lx, y); - vertices[fillVertexCount].color = fillColor; - ++fillVertexCount; + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); + smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); + smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); + + dy = part ? delta : -delta; + if (penWidth) { + smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c); + smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c); + smoothVertices[index++].set(rX, Y, transparent, delta, dy); + smoothVertices[index++].set(lX, Y, transparent, -delta, dy); + + indices[--outerAAHead] = index - 2; + indices[--outerAAHead] = index - 4; + indices[outerAATail++] = index - 3; + indices[outerAATail++] = index - 1; + } else { + smoothVertices[index++].set(rx, y, transparent, delta, dy); + smoothVertices[index++].set(lx, y, transparent, -delta, dy); + } + } else { + vertices[index++].set(rx, y, fillColor); + vertices[index++].set(lx, y, fillColor); + if (penWidth) { + vertices[index++].set(rx, y, borderColor); + vertices[index++].set(lx, y, borderColor); + vertices[index++].set(rX, Y, borderColor); + vertices[index++].set(lX, Y, borderColor); + } } + py = y; plx = lx; prx = rx; @@ -413,25 +599,29 @@ void QSGDefaultRectangleNode::updateGeometry() s = s * cosStep + tmp * sinStep; } } + Q_ASSERT(index == vertexCount); + // Close the triangle strips. + if (m_antialiasing) { + indices[--innerAAHead] = indices[innerAATail - 1]; + indices[--innerAAHead] = indices[innerAATail - 2]; + Q_ASSERT(innerAATail <= indexCount); + } if (penWidth) { - // Close border. - const Vertex &first = borderVertices[borderVertexHead]; - const Vertex &second = borderVertices[borderVertexHead + 1]; - borderVertices[borderVertexTail++] = first; - borderVertices[borderVertexTail++] = second; - - Q_ASSERT(borderVertexHead == 0 && borderVertexTail == borderGeometry->vertexCount()); + indices[--borderHead] = indices[borderTail - 1]; + indices[--borderHead] = indices[borderTail - 2]; + Q_ASSERT(borderTail <= indexCount); + } + if (m_antialiasing && penWidth) { + indices[--outerAAHead] = indices[outerAATail - 1]; + indices[--outerAAHead] = indices[outerAATail - 2]; + Q_ASSERT(outerAATail == indexCount); } - Q_ASSERT(fillVertexCount == fill->vertexCount()); - } else { - // Straight corners. QRectF innerRect = m_rect; QRectF outerRect = m_rect; - qreal halfPenWidth = 0; if (penWidth) { if (m_aligned && (int(penWidth) & 1)) { // Pen width is odd, so add the offset as documented. @@ -439,61 +629,114 @@ void QSGDefaultRectangleNode::updateGeometry() innerRect.moveTop(innerRect.top() + qreal(0.5)); outerRect = innerRect; } - halfPenWidth = penWidth * qreal(0.5); - innerRect.adjust(halfPenWidth, halfPenWidth, -halfPenWidth, -halfPenWidth); - outerRect.adjust(-halfPenWidth, -halfPenWidth, halfPenWidth, halfPenWidth); + innerRect.adjust(0.5f * penWidth, 0.5f * penWidth, -0.5f * penWidth, -0.5f * penWidth); + outerRect.adjust(-0.5f * penWidth, -0.5f * penWidth, 0.5f * penWidth, 0.5f * penWidth); } - int nextGradientStop = 0; - qreal gradientPos = halfPenWidth / m_rect.height(); - while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) - ++nextGradientStop; - int lastGradientStop = stops.size() - 1; - qreal lastGradientPos = (m_rect.height() - halfPenWidth) / m_rect.height(); - while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) - --lastGradientStop; - - int borderVertexCount = 0; + float delta = qMin(width, height) * 0.5f; + int innerVertexCount = 4 + gradientIntersections * 2; + int outerVertexCount = 4; + int vertexCount = innerVertexCount; + if (m_antialiasing || penWidth) + vertexCount += innerVertexCount; + if (penWidth) + vertexCount += outerVertexCount; + if (m_antialiasing && penWidth) + vertexCount += outerVertexCount; + + int fillIndexCount = innerVertexCount; + int innerAAIndexCount = innerVertexCount * 2 + 2; + int borderIndexCount = innerVertexCount * 2 + 2; + int outerAAIndexCount = outerVertexCount * 2 + 2; + int indexCount = 0; + int fillHead = 0; + int innerAAHead = 0; + int innerAATail = 0; + int borderHead = 0; + int borderTail = 0; + int outerAAHead = 0; + int outerAATail = 0; + bool hasFill = m_color.rgba() != 0 || !stops.isEmpty(); + if (hasFill) + indexCount += fillIndexCount; + if (m_antialiasing) { + innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; + indexCount += innerAAIndexCount; + } if (penWidth) { - borderGeometry->allocate((1 + lastGradientStop - nextGradientStop) * 4 + 10); - borderVertices = (Vertex *)borderGeometry->vertexData(); + borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; + indexCount += borderIndexCount; + } + if (m_antialiasing && penWidth) { + outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; + indexCount += outerAAIndexCount; } - fill->allocate((3 + lastGradientStop - nextGradientStop) * 2); - fillVertices = (uchar *)fill->vertexData(); - QVarLengthArray<qreal, 16> ys(3 + lastGradientStop - nextGradientStop); - int yCount = 0; + g->allocate(vertexCount, indexCount); + vertices = reinterpret_cast<Vertex *>(g->vertexData()); + memset(vertices, 0, vertexCount * vertexStride); + quint16 *indices = g->indexDataAsUShort(); + quint16 index = 0; - for (int part = 0; part < 2; ++part) { - qreal y = (part ? innerRect.bottom() : innerRect.top()); - gradientPos = (y - innerRect.top() + halfPenWidth) / m_rect.height(); + float lx = innerRect.left(); + float rx = innerRect.right(); + float lX = outerRect.left(); + float rX = outerRect.right(); + + for (int part = -1; part <= 1; part += 2) { + float y = (part == 1 ? innerRect.bottom() : innerRect.top()); + float Y = (part == 1 ? outerRect.bottom() : outerRect.top()); + gradientPos = (y - innerRect.top() + 0.5f * penWidth) / height; while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { // Insert vertices at gradient stops. - qreal gy = (innerRect.top() - halfPenWidth) + stops.at(nextGradientStop).first * m_rect.height(); - Q_ASSERT(fillVertexCount >= 2); - - ColorVertex *vertices = (ColorVertex *)fillVertices; + float gy = (innerRect.top() - 0.5f * penWidth) + stops.at(nextGradientStop).first * height; fillColor = colorToColor4ub(stops.at(nextGradientStop).second); - vertices[fillVertexCount].position = QVector2D(innerRect.right(), gy); - vertices[fillVertexCount].color = fillColor; - ++fillVertexCount; - vertices[fillVertexCount].position = QVector2D(innerRect.left(), gy); - vertices[fillVertexCount].color = fillColor; - ++fillVertexCount; - ys[yCount++] = gy; + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; + } + + if (penWidth) { + --borderHead; + indices[borderHead] = indices[borderHead + 2]; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail] = indices[borderTail - 2]; + ++borderTail; + } + + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + bool lower = stops.at(nextGradientStop).first > 0.5f; + float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); + smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy); + smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy); + if (penWidth) { + smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); + smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); + } else { + smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta); + smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta); + } + } else { + vertices[index++].set(rx, gy, fillColor); + vertices[index++].set(lx, gy, fillColor); + if (penWidth) { + vertices[index++].set(rx, gy, borderColor); + vertices[index++].set(lx, gy, borderColor); + } + } ++nextGradientStop; } - if (stops.isEmpty()) { - Q_ASSERT(m_material_type == TypeFlat); - Vertex *vertices = (Vertex *)fillVertices; - vertices[fillVertexCount++].position = QVector2D(innerRect.right(), y); - vertices[fillVertexCount++].position = QVector2D(innerRect.left(), y); - } else { + if (!stops.isEmpty()) { if (nextGradientStop == 0) { fillColor = colorToColor4ub(stops.at(0).second); } else if (nextGradientStop == stops.size()) { @@ -501,43 +744,78 @@ void QSGDefaultRectangleNode::updateGeometry() } else { const QGradientStop &prev = stops.at(nextGradientStop - 1); const QGradientStop &next = stops.at(nextGradientStop); - qreal t = (gradientPos - prev.first) / (next.first - prev.first); - fillColor = (colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t); + float t = (gradientPos - prev.first) / (next.first - prev.first); + fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t; } - - ColorVertex *vertices = (ColorVertex *)fillVertices; - vertices[fillVertexCount].position = QVector2D(innerRect.right(), y); - vertices[fillVertexCount].color = fillColor; - ++fillVertexCount; - vertices[fillVertexCount].position = QVector2D(innerRect.left(), y); - vertices[fillVertexCount].color = fillColor; - ++fillVertexCount; } - ys[yCount++] = y; - } - - if (penWidth) { - borderVertices[borderVertexCount++].position = QVector2D(outerRect.right(), outerRect.top()); - borderVertices[borderVertexCount++].position = QVector2D(innerRect.right(), ys[0]); - for (int i = 1; i < fillVertexCount / 2; ++i) { - borderVertices[borderVertexCount++].position = QVector2D(outerRect.right(), outerRect.bottom()); - borderVertices[borderVertexCount++].position = QVector2D(innerRect.right(), ys[i]); + if (hasFill) { + indices[fillHead++] = index; + indices[fillHead++] = index + 1; } - borderVertices[borderVertexCount++].position = QVector2D(outerRect.left(), outerRect.bottom()); - borderVertices[borderVertexCount++].position = QVector2D(innerRect.left(), ys[fillVertexCount / 2 - 1]); - for (int i = fillVertexCount / 2 - 2; i >= 0; --i) { - borderVertices[borderVertexCount++].position = QVector2D(outerRect.left(), outerRect.top()); - borderVertices[borderVertexCount++].position = QVector2D(innerRect.left(), ys[i]); + if (penWidth) { + indices[--borderHead] = index + 4; + indices[--borderHead] = index + 2; + indices[borderTail++] = index + 3; + indices[borderTail++] = index + 5; } - borderVertices[borderVertexCount++].position = QVector2D(outerRect.right(), outerRect.top()); - borderVertices[borderVertexCount++].position = QVector2D(innerRect.right(), innerRect.top()); + if (m_antialiasing) { + indices[--innerAAHead] = index + 2; + indices[--innerAAHead] = index; + indices[innerAATail++] = index + 1; + indices[innerAATail++] = index + 3; + + float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); + smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); + smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); + + if (penWidth) { + smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part); + smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part); + smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part); + smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part); + smoothVertices[index++].set(rX, Y, transparent, delta, delta * part); + smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part); + + indices[--outerAAHead] = index - 2; + indices[--outerAAHead] = index - 4; + indices[outerAATail++] = index - 3; + indices[outerAATail++] = index - 1; + } else { + smoothVertices[index++].set(rx, y, transparent, delta, delta * part); + smoothVertices[index++].set(lx, y, transparent, -delta, delta * part); + } + } else { + vertices[index++].set(rx, y, fillColor); + vertices[index++].set(lx, y, fillColor); + if (penWidth) { + vertices[index++].set(rx, y, borderColor); + vertices[index++].set(lx, y, borderColor); + vertices[index++].set(rX, Y, borderColor); + vertices[index++].set(lX, Y, borderColor); + } + } + } + Q_ASSERT(index == vertexCount); - Q_ASSERT(borderVertexCount == borderGeometry->vertexCount()); + // Close the triangle strips. + if (m_antialiasing) { + indices[--innerAAHead] = indices[innerAATail - 1]; + indices[--innerAAHead] = indices[innerAATail - 2]; + Q_ASSERT(innerAATail <= indexCount); + } + if (penWidth) { + indices[--borderHead] = indices[borderTail - 1]; + indices[--borderHead] = indices[borderTail - 2]; + Q_ASSERT(borderTail <= indexCount); + } + if (m_antialiasing && penWidth) { + indices[--outerAAHead] = indices[outerAATail - 1]; + indices[--outerAAHead] = indices[outerAATail - 2]; + Q_ASSERT(outerAATail == indexCount); } - Q_ASSERT(fillVertexCount == fill->vertexCount()); } markDirty(DirtyGeometry); diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h index faa2b6a43c..ed93c10790 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h +++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h @@ -45,20 +45,30 @@ #include <private/qsgadaptationlayer_p.h> -#include <QtQuick/qsgflatcolormaterial.h> +#include <QtQuick/qsgvertexcolormaterial.h> QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QSGMaterial; class QSGContext; +class SmoothColorMaterial : public QSGMaterial +{ +public: + SmoothColorMaterial(); + + int compare(const QSGMaterial *other) const; + +protected: + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; +}; + class QSGDefaultRectangleNode : public QSGRectangleNode { public: QSGDefaultRectangleNode(); - ~QSGDefaultRectangleNode(); virtual void setRect(const QRectF &rect); virtual void setColor(const QColor &color); @@ -66,35 +76,30 @@ public: virtual void setPenWidth(qreal width); virtual void setGradientStops(const QGradientStops &stops); virtual void setRadius(qreal radius); + virtual void setAntialiasing(bool antialiasing); virtual void setAligned(bool aligned); virtual void update(); private: - enum { - TypeFlat, - TypeVertexGradient - }; - QSGGeometryNode *border(); - void updateGeometry(); void updateGradientTexture(); - QSGGeometryNode *m_border; - QSGFlatColorMaterial m_border_material; - QSGFlatColorMaterial m_fill_material; + QSGVertexColorMaterial m_material; + SmoothColorMaterial m_smoothMaterial; QRectF m_rect; QGradientStops m_gradient_stops; + QColor m_color; + QColor m_border_color; qreal m_radius; qreal m_pen_width; uint m_aligned : 1; + uint m_antialiasing : 1; uint m_gradient_is_opaque : 1; uint m_dirty_geometry : 1; - uint m_material_type : 2; // Only goes up to 3 - - QSGGeometry m_default_geometry; + QSGGeometry m_geometry; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp index 7f461bb72d..cdbef7d88f 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.cpp +++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp @@ -355,19 +355,6 @@ static const char qt_scenegraph_texture_material_opacity_fragment[] = " gl_FragColor = texture2D(qt_Texture, qt_TexCoord) * opacity; \n" "}"; -class QSGTextureMaterialShader : public QSGOpaqueTextureMaterialShader -{ -public: - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual void initialize(); - - static QSGMaterialType type; - -protected: - virtual const char *fragmentShader() const { return qt_scenegraph_texture_material_opacity_fragment; } - - int m_opacity_id; -}; QSGMaterialType QSGTextureMaterialShader::type; @@ -407,4 +394,9 @@ void QSGTextureMaterialShader::initialize() m_opacity_id = program()->uniformLocation("opacity"); } +const char *QSGTextureMaterialShader::fragmentShader() const +{ + return qt_scenegraph_texture_material_opacity_fragment; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgtexturematerial_p.h b/src/quick/scenegraph/util/qsgtexturematerial_p.h index fe14726de7..b8a751080e 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial_p.h +++ b/src/quick/scenegraph/util/qsgtexturematerial_p.h @@ -65,6 +65,20 @@ protected: int m_matrix_id; }; +class QSGTextureMaterialShader : public QSGOpaqueTextureMaterialShader +{ +public: + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual void initialize(); + + static QSGMaterialType type; + +protected: + virtual const char *fragmentShader() const; + + int m_opacity_id; +}; + QT_END_NAMESPACE QT_END_HEADER |