aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/items.pri2
-rw-r--r--src/quick/items/qquickborderimage.cpp70
-rw-r--r--src/quick/items/qquickimage.cpp11
-rw-r--r--src/quick/items/qquickitem.cpp113
-rw-r--r--src/quick/items/qquickitem.h5
-rw-r--r--src/quick/items/qquickitem_p.h4
-rw-r--r--src/quick/items/qquickninepatchnode.cpp305
-rw-r--r--src/quick/items/qquickninepatchnode_p.h98
-rw-r--r--src/quick/items/qquickrectangle.cpp1
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp2
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.h5
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h12
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp1
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode.cpp605
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode_p.h26
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode.cpp854
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode_p.h35
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp18
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial_p.h14
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