aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/qsgdefaultimagenode.cpp
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/qsgdefaultimagenode.cpp
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/qsgdefaultimagenode.cpp')
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode.cpp605
1 files changed, 516 insertions, 89 deletions
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);