aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph
diff options
context:
space:
mode:
authorKim Motoyoshi Kalland <kim.kalland@nokia.com>2012-06-29 17:14:10 +0200
committerQt by Nokia <qt-info@nokia.com>2012-07-13 11:25:25 +0200
commitd83eb21fb296b73bd111d907dfb9ecde373b9bb3 (patch)
treefbe6b8be85e5a52094a310a72fe5575b7e4131ee /src/quick/scenegraph
parent60a13ee3fd021080d92a11b3456602103ad61a79 (diff)
Change antialiasing method for QML2.
Since multisampling can require a lot of memory, and might not be supported on some hardware, turn off multisampling and implement antialiasing in the vertex shader instead. The alternative method of antialiasing is implemented for Rectangle, Image, BorderImage and AnimatedImage, and must be explicitly enabled by setting the new antialiasing property. Task-number: QTBUG-26268 Change-Id: I39a93d978658a494bf51e9f0fd02d8414eb8be12 Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
Diffstat (limited to 'src/quick/scenegraph')
-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
9 files changed, 1156 insertions, 414 deletions
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