From d83eb21fb296b73bd111d907dfb9ecde373b9bb3 Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Fri, 29 Jun 2012 17:14:10 +0200 Subject: [PATCH] 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 --- src/quick/items/items.pri | 2 - src/quick/items/qquickborderimage.cpp | 70 +- src/quick/items/qquickimage.cpp | 11 +- src/quick/items/qquickitem.cpp | 113 ++- src/quick/items/qquickitem.h | 5 + src/quick/items/qquickitem_p.h | 4 +- src/quick/items/qquickninepatchnode.cpp | 305 -------- src/quick/items/qquickninepatchnode_p.h | 98 --- src/quick/items/qquickrectangle.cpp | 1 + src/quick/items/qquickshadereffectsource.cpp | 2 +- src/quick/scenegraph/coreapi/qsgmaterial.h | 5 +- src/quick/scenegraph/qsgadaptationlayer_p.h | 12 +- src/quick/scenegraph/qsgcontext.cpp | 1 - src/quick/scenegraph/qsgdefaultimagenode.cpp | 605 +++++++++++++--- src/quick/scenegraph/qsgdefaultimagenode_p.h | 26 +- src/quick/scenegraph/qsgdefaultrectanglenode.cpp | 854 +++++++++++++++-------- src/quick/scenegraph/qsgdefaultrectanglenode_p.h | 35 +- src/quick/scenegraph/util/qsgtexturematerial.cpp | 18 +- src/quick/scenegraph/util/qsgtexturematerial_p.h | 14 + 19 files changed, 1316 insertions(+), 865 deletions(-) delete mode 100644 src/quick/items/qquickninepatchnode.cpp delete mode 100644 src/quick/items/qquickninepatchnode_p.h diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 4f22b32..9f205ed 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 abd20c6..889877a 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 #include @@ -565,29 +564,70 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat return 0; } - QQuickNinePatchNode *node = static_cast(oldNode); + QSGImageNode *node = static_cast(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(0, d->pix.width() - border->right() - border->left()) / d->pix.width(), + qMax(0, d->pix.height() - border->bottom() - border->top()) / d->pix.height()); + innerTargetRect = QRectF(border->left(), + border->top(), + qMax(0, width() - border->right() - border->left()), + qMax(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 aa68477..f4089be 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 d3c8973..a41e451 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 7174edd..15bf5dd 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 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 ddebefd..6909921 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 d304e2d..0000000 --- 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 -#include - -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; ycx = m_targetRect.width() - v->x; - ++v; - } - } - -// v = m_geometry.vertexDataAsTexturedPoint2D(); -// for (int i=0; ix, v->y, v->tx, v->ty); -// ++v; -// } - - quint16 *i = m_geometry.indexDataAsUShort(); - int row = xTotalChunkCount * 2; - for (int r=0; rset(0, y, tsr.left(), ty); - v++->set(m_innerRect.x(), y, xTexChunk1, ty); - - for (int xc=0; xcset(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 c2761f7..0000000 --- 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 -#include -#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 608fd94..ec300e8 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 3f36c86..20b9190 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 450dd28..a421bb5 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 c1b2afc..76e2fea 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 dec9ea9..47cc667 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 e6855c4..2ccf9d9 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode.cpp +++ b/src/quick/scenegraph/qsgdefaultimagenode.cpp @@ -41,16 +41,163 @@ #include "qsgdefaultimagenode_p.h" -#include - #include #include #include +#include +#include +#include + 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(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 xData(2 * hCells); QVarLengthArray 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(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 9062aff..87b3f77 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode_p.h +++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h @@ -44,20 +44,35 @@ #define DEFAULT_PIXMAPNODE_H #include - #include 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 18cac36..b204fff 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(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(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 ys(3 + lastGradientStop - nextGradientStop); - int yCount = 0; + g->allocate(vertexCount, indexCount); + vertices = reinterpret_cast(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 faa2b6a..ed93c10 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h +++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h @@ -45,20 +45,30 @@ #include -#include +#include 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 7f461bb..cdbef7d 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 fe14726..b8a7510 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 -- 2.7.4