Add a way of transforming texture coordinates of a simple textured quad
authorSean Harmer <sean.harmer@kdab.com>
Mon, 15 Apr 2013 10:52:24 +0000 (11:52 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Tue, 16 Apr 2013 13:41:31 +0000 (15:41 +0200)
This commit introduces the enum TextureCoordinatesTransformFlag and
corresponding QFlags OR combination TextureCoordinatesTransformMode.
This enum is used to control the orientation of texture coordinates
relative to window/item coordinates.

The common use case addressed by this commit is when rendering to a
texture via an FBO using some 3rd party OpenGL library. Some libraries
do not offer a way to orient the rendered output which results in the
texture being displayed upside down when used in conjunction with
QSGSimpleTextureNode.

There are a number of possible solutions to this:

1 Mirror the item by scaling by -1 in the y-direction in QML document

2 Use a custom material (shader) that transforms texture coordinates
  in GLSL

3 Generate texture coordinates differently

This commit opts for approach 3. Approach 1 is ugly and visible to the
end user and also causes more work when other transformations interact
with the necessary scaling. Approach 2 has a performance cost in both
switching material (shader) and also in additional per-vertex or per-
fragment operations. The chosen approach hides it from the end user
and has zero runtime cost delta compared to any other textured quad.

Change-Id: I95870da50a09d113aeff2681bfd458669ec7a5a4
Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>

src/quick/scenegraph/util/qsgsimpletexturenode.cpp
src/quick/scenegraph/util/qsgsimpletexturenode.h

index 318120e..86e0d36 100644 (file)
 
 
 #include "qsgsimpletexturenode.h"
+#include <private/qsgnode_p.h>
 
 QT_BEGIN_NAMESPACE
 
+class QSGSimpleTextureNodePrivate : public QSGGeometryNodePrivate
+{
+public:
+    QSGSimpleTextureNodePrivate()
+        : QSGGeometryNodePrivate()
+        , m_texCoordMode(QSGSimpleTextureNode::NoTransform)
+    {}
+
+    QSGSimpleTextureNode::TextureCoordinatesTransformMode m_texCoordMode;
+};
+
 static void qsgsimpletexturenode_update(QSGGeometry *g,
                                         QSGTexture *texture,
-                                        const QRectF &rect)
+                                        const QRectF &rect,
+                                        QSGSimpleTextureNode::TextureCoordinatesTransformMode texCoordMode)
 {
     if (!texture)
         return;
 
     QSize ts = texture->textureSize();
     QRectF sourceRect(0, 0, ts.width(), ts.height());
+
+    // Maybe transform the texture coordinates
+    if (texCoordMode.testFlag(QSGSimpleTextureNode::MirrorHorizontally)) {
+        float tmp = sourceRect.left();
+        sourceRect.setLeft(sourceRect.right());
+        sourceRect.setRight(tmp);
+    }
+    if (texCoordMode.testFlag(QSGSimpleTextureNode::MirrorVertically)) {
+        float tmp = sourceRect.top();
+        sourceRect.setTop(sourceRect.bottom());
+        sourceRect.setBottom(tmp);
+    }
+
     QSGGeometry::updateTexturedRectGeometry(g, rect, texture->convertToNormalizedSourceRect(sourceRect));
 }
 
@@ -71,7 +97,8 @@ static void qsgsimpletexturenode_update(QSGGeometry *g,
     Constructs a new simple texture node
  */
 QSGSimpleTextureNode::QSGSimpleTextureNode()
-    : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+    : QSGGeometryNode(*new QSGSimpleTextureNodePrivate)
+    , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
 {
     setGeometry(&m_geometry);
     setMaterial(&m_material);
@@ -112,7 +139,8 @@ void QSGSimpleTextureNode::setRect(const QRectF &r)
     if (m_rect == r)
         return;
     m_rect = r;
-    qsgsimpletexturenode_update(&m_geometry, texture(), m_rect);
+    Q_D(QSGSimpleTextureNode);
+    qsgsimpletexturenode_update(&m_geometry, texture(), m_rect, d->m_texCoordMode);
     markDirty(DirtyGeometry);
 }
 
@@ -144,7 +172,8 @@ void QSGSimpleTextureNode::setTexture(QSGTexture *texture)
         return;
     m_material.setTexture(texture);
     m_opaque_material.setTexture(texture);
-    qsgsimpletexturenode_update(&m_geometry, texture, m_rect);
+    Q_D(QSGSimpleTextureNode);
+    qsgsimpletexturenode_update(&m_geometry, texture, m_rect, d->m_texCoordMode);
     markDirty(DirtyMaterial);
 }
 
@@ -158,4 +187,48 @@ QSGTexture *QSGSimpleTextureNode::texture() const
     return m_material.texture();
 }
 
+/*!
+    \enum QSGSimpleTextureNode::TextureCoordinatesTransformFlag
+
+    The TextureCoordinatesTransformFlag enum is used to specify the
+    mode used to generate texture coordinates for a textured quad.
+
+    \value NoTransform          Texture coordinates are oriented with window coordinates
+                                i.e. with origin at top-left.
+
+    \value MirrorHorizontally   Texture coordinates are inverted in the horizontal axis with
+                                respect to window coordinates
+
+    \value MirrorVertically     Texture coordinates are inverted in the vertical axis with
+                                respect to window coordinates
+ */
+
+/*!
+    Sets the method used to generate texture coordinates to \a mode. This can be used to obtain
+    correct orientation of the texture. This is commonly needed when using a third party OpenGL
+    library to render to texture as OpenGL has an inverted y-axis relative to Qt Quick.
+
+    \sa textureCoordinatesTransform()
+ */
+void QSGSimpleTextureNode::setTextureCoordinatesTransform(QSGSimpleTextureNode::TextureCoordinatesTransformMode mode)
+{
+    Q_D(QSGSimpleTextureNode);
+    if (d->m_texCoordMode == mode)
+        return;
+    d->m_texCoordMode = mode;
+    qsgsimpletexturenode_update(&m_geometry, texture(), m_rect, d->m_texCoordMode);
+    markDirty(DirtyMaterial);
+}
+
+/*!
+    Returns the mode used to generate texture coordinates for this node.
+
+    \sa setTextureCoordinatesTransform()
+ */
+QSGSimpleTextureNode::TextureCoordinatesTransformMode QSGSimpleTextureNode::textureCoordinatesTransform() const
+{
+    Q_D(const QSGSimpleTextureNode);
+    return d->m_texCoordMode;
+}
+
 QT_END_NAMESPACE
index ffd1021..f5ab7fb 100644 (file)
@@ -48,6 +48,8 @@
 
 QT_BEGIN_NAMESPACE
 
+class QSGSimpleTextureNodePrivate;
+
 class Q_QUICK_EXPORT QSGSimpleTextureNode : public QSGGeometryNode
 {
 public:
@@ -63,14 +65,28 @@ public:
     void setFiltering(QSGTexture::Filtering filtering);
     QSGTexture::Filtering filtering() const;
 
+    enum TextureCoordinatesTransformFlag {
+        NoTransform        = 0x00,
+        MirrorHorizontally = 0x01,
+        MirrorVertically   = 0x02
+    };
+    Q_DECLARE_FLAGS(TextureCoordinatesTransformMode, TextureCoordinatesTransformFlag)
+
+    void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode);
+    TextureCoordinatesTransformMode textureCoordinatesTransform() const;
+
 private:
     QSGGeometry m_geometry;
     QSGOpaqueTextureMaterial m_opaque_material;
     QSGTextureMaterial m_material;
 
     QRectF m_rect;
+
+    Q_DECLARE_PRIVATE(QSGSimpleTextureNode)
 };
 
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGSimpleTextureNode::TextureCoordinatesTransformMode)
+
 QT_END_NAMESPACE
 
 #endif // QSGSIMPLETEXTURENODE_H