From 97ba54f16a6589bde42f0be66b9059cbf7203e4d Mon Sep 17 00:00:00 2001
From: Pasi Keranen <pasi.keranen@digia.com>
Date: Thu, 18 Sep 2014 18:05:39 +0300
Subject: [PATCH] Added some missing functions and flip-Y unpack extension.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Listed all missing functions and implemented drawingBufferWidth(), drawingBufferHeight(), getActiveAttrib(), getActiveUniform() functions.
Removed default Y-flip and implemented support for UNPACK_FLIP_Y_WEBGL, this breaks functionality but now behavior is consistent with the spec.

Change-Id: Ie6ff879f5b7acea713ab4729eccc6c8948ad394f
Reviewed-by: Tomi Korpipää <tomi.korpipaa@digia.com>
Reviewed-by: Pasi Keränen <pasi.keranen@digia.com>
---
 src/activeinfo3d.cpp     |  96 ++++++++
 src/activeinfo3d_p.h     |  80 +++++++
 src/context3d.cpp        | 479 +++++++++++++++++++++++++++++++--------
 src/context3d_p.h        |  45 ++++
 src/qcanvas3d_plugin.cpp |   4 +
 src/qcanvas3d_plugin.h   |   2 +
 src/src.pro              |   6 +-
 src/teximage3d.cpp       |  41 ++--
 src/teximage3d_p.h       |   4 +-
 9 files changed, 641 insertions(+), 116 deletions(-)
 create mode 100644 src/activeinfo3d.cpp
 create mode 100644 src/activeinfo3d_p.h

diff --git a/src/activeinfo3d.cpp b/src/activeinfo3d.cpp
new file mode 100644
index 0000000..be76580
--- /dev/null
+++ b/src/activeinfo3d.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later 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 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "activeinfo3d_p.h"
+
+/*!
+ * \qmltype ActiveInfo3D
+ * \since QtCanvas3D 1.0
+ * \ingroup qtcanvas3d-qml-types
+ * \brief Active attribute or uniform information.
+ *
+ * The ActiveInfo3D interface represents the information returned from the getActiveAttrib and
+ * getActiveUniform calls.
+ *
+ * \sa Context3D, Canvas3D, {QML Canvas 3D QML Types}
+ */
+
+
+
+CanvasActiveInfo::CanvasActiveInfo(int size, CanvasContext::glEnums type,
+                                   QString name, QObject *parent) :
+    QObject(parent),
+    m_size(size),
+    m_type(type),
+    m_name(name)
+{
+}
+
+/*!
+ * \qmlproperty int ActiveInfo3D::size
+ * Size of the active attrib or uniform.
+ */
+/*!
+ * \internal
+ */
+int CanvasActiveInfo::size() const
+{
+    return m_size;
+}
+
+/*!
+ * \qmlproperty Context3D.glEnums ActiveInfo3D::type
+ * Type of the active attrib or uniform.
+ */
+/*!
+ * \internal
+ */
+CanvasContext::glEnums CanvasActiveInfo::type() const
+{
+    return m_type;
+}
+
+/*!
+ * \qmlproperty string ActiveInfo3D::name
+ * Name of the active attrib or uniform.
+ */
+/*!
+ * \internal
+ */
+QString CanvasActiveInfo::name() const
+{
+    return m_name;
+}
diff --git a/src/activeinfo3d_p.h b/src/activeinfo3d_p.h
new file mode 100644
index 0000000..40cd1ed
--- /dev/null
+++ b/src/activeinfo3d_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later 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 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the QtCanvas3D API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef CANVASACTIVEINFO_P_H
+#define CANVASACTIVEINFO_P_H
+
+#include <QObject>
+#include "context3d_p.h"
+
+class CanvasActiveInfo : public QObject
+{
+    Q_OBJECT
+
+    Q_PROPERTY(int size READ size NOTIFY sizeChanged)
+    Q_PROPERTY(CanvasContext::glEnums type READ type NOTIFY typeChanged)
+    Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+
+public:
+    explicit CanvasActiveInfo(int size, CanvasContext::glEnums type,
+                              QString name, QObject *parent = 0);
+
+    int size() const;
+    CanvasContext::glEnums type() const;
+    QString name() const;
+
+signals:
+    void sizeChanged(int size);
+    void typeChanged(CanvasContext::glEnums type);
+    void nameChanged(QString &name);
+
+private:
+    int m_size;
+    CanvasContext::glEnums m_type;
+    QString m_name;
+};
+
+#endif // CANVASACTIVEINFO_P_H
diff --git a/src/context3d.cpp b/src/context3d.cpp
index 5b91837..1ed9d68 100644
--- a/src/context3d.cpp
+++ b/src/context3d.cpp
@@ -34,6 +34,7 @@
 **
 ****************************************************************************/
 
+#include "activeinfo3d_p.h"
 #include "canvas3d_p.h"
 #include "context3d_p.h"
 #include "texture3d_p.h"
@@ -78,6 +79,7 @@
 CanvasContext::CanvasContext(QOpenGLContext *context, int width, int height, QObject *parent) :
     QObject(parent),
     QOpenGLFunctions(context),
+    m_unpackFlipYEnabled(false),
     m_logAllCalls(false),
     m_logAllErrors(true),
     m_glViewportRect(0, 0, width, height),
@@ -123,8 +125,17 @@ CanvasContext::~CanvasContext()
 void CanvasContext::setCanvas(Canvas *canvas)
 {
     if (m_canvas != canvas) {
+        if (m_canvas) {
+            disconnect(m_canvas, &QQuickItem::widthChanged, this, 0);
+            disconnect(m_canvas, &QQuickItem::heightChanged, this, 0);
+        }
+
+
         m_canvas = canvas;
         emit canvasChanged(canvas);
+
+        connect(m_canvas, &QQuickItem::widthChanged, this, &CanvasContext::drawingBufferWidthChanged);
+        connect(m_canvas, &QQuickItem::heightChanged, this, &CanvasContext::drawingBufferHeightChanged);
     }
 }
 
@@ -133,6 +144,36 @@ Canvas *CanvasContext::canvas()
     return m_canvas;
 }
 
+/*!
+ * \qmlproperty bool Context3D::drawingBufferWidth
+ * Returns the current logical pixel width of the drawing buffer. To get width in physical pixels
+ * you need to multiply this with the devicePixelRatio.
+ */
+uint CanvasContext::drawingBufferWidth()
+{
+    uint width = 0;
+    if (m_canvas)
+        width = m_canvas->width();
+
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(): " << width;
+    return width;
+}
+
+/*!
+ * \qmlproperty bool Context3D::drawingBufferHeight
+ * Returns the current logical pixel height of the drawing buffer. To get height in physical pixels
+ * you need to multiply this with the devicePixelRatio.
+ */
+uint CanvasContext::drawingBufferHeight()
+{
+    uint height = 0;
+    if (m_canvas)
+        height = m_canvas->height();
+
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(): " << height;
+    return height;
+}
+
 /*!
  * \internal
  */
@@ -567,18 +608,22 @@ void CanvasContext::compressedTexSubImage2D(glEnums target, int level,
     Q_UNUSED(pixels)
 
     if (!pixels) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION No current texture bound";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION No current texture bound";
         m_error = INVALID_OPERATION;
     }
 
     if (!m_currentTexture) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION No current texture bound";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION No current texture bound";
         m_error = INVALID_OPERATION;
     } else if (!m_currentTexture->isAlive()) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION Currently bound texture is already deleted";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION Currently bound texture is already deleted";
         m_error = INVALID_OPERATION;
     } else {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM Core WebGL API doesn't support compressed images";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_ENUM Core WebGL API doesn't support compressed images";
         m_error = INVALID_ENUM;
     }
 }
@@ -614,10 +659,12 @@ void CanvasContext::copyTexImage2D(glEnums target, int level, glEnums internalfo
                                    int border)
 {
     if (!m_currentTexture) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION No current texture bound";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION No current texture bound";
         m_error = INVALID_OPERATION;
     } else if (!m_currentTexture->isAlive()) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION Currently bound texture is already deleted";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION Currently bound texture is already deleted";
         m_error = INVALID_OPERATION;
     } else {
         glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
@@ -651,10 +698,12 @@ void CanvasContext::copyTexSubImage2D(glEnums target, int level,
                                       int width, int height)
 {
     if (!m_currentTexture) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION No current texture bound";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION No current texture bound";
         m_error = INVALID_OPERATION;
     } else if (!m_currentTexture->isAlive()) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION Currently bound texture is already deleted";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION Currently bound texture is already deleted";
         m_error = INVALID_OPERATION;
     } else {
         copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
@@ -695,21 +744,25 @@ void CanvasContext::texImage2D(glEnums target, int level, glEnums internalformat
                                glEnums format, glEnums type,
                                CanvasTypedArray *pixels)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "( target:" << glEnumToString(target) <<
-                                   ", level:" << level <<
-                                   ", internalformat:" << glEnumToString(internalformat) <<
-                                   ", width:" << width <<
-                                   ", height:" << height <<
-                                   ", border:" << border <<
-                                   ", format:" << glEnumToString(format) <<
-                                   ", type:" << glEnumToString(type) <<
-                                   ", pixels:" << pixels << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "( target:" << glEnumToString(target)
+                                << ", level:" << level
+                                << ", internalformat:" << glEnumToString(internalformat)
+                                << ", width:" << width
+                                << ", height:" << height
+                                << ", border:" << border
+                                << ", format:" << glEnumToString(format)
+                                << ", type:" << glEnumToString(type)
+                                << ", pixels:" << pixels << ")";
     if (!m_currentTexture) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION No current texture bound";
+        if (m_logAllErrors) qDebug() << "Context3D::"
+                                     << __FUNCTION__
+                                     << ":INVALID_OPERATION No current texture bound";
         m_error = INVALID_OPERATION;
         return;
     } else if (!m_currentTexture->isAlive()) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION Currently bound texture is already deleted";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION Currently bound texture is already deleted";
         m_error = INVALID_OPERATION;
         return;
     }
@@ -725,29 +778,73 @@ void CanvasContext::texImage2D(glEnums target, int level, glEnums internalformat
 
     QString className(pixels->metaObject()->className());
 
+    int bytesPerPixel = 0;
     switch (type) {
-    case UNSIGNED_BYTE:
+    case UNSIGNED_BYTE: {
         if (className != "CanvasUint8Array") {
-            if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION Invalid TypedArray supplied, expected Uint8Array, received " << pixels->metaObject()->className();
+            if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                         << ":INVALID_OPERATION Invalid TypedArray supplied, expected Uint8Array, received "
+                                         << pixels->metaObject()->className();
             m_error = INVALID_OPERATION;
             return;
         }
+
+        switch (format) {
+        case ALPHA:
+            bytesPerPixel = 1;
+            break;
+        case RGB:
+            bytesPerPixel = 3;
+            break;
+        case RGBA:
+            bytesPerPixel = 4;
+            break;
+        case LUMINANCE:
+            bytesPerPixel = 1;
+            break;
+        case LUMINANCE_ALPHA:
+            bytesPerPixel = 2;
+            break;
+        default:
+            break;
+        }
+
+        if (bytesPerPixel == 0) {
+            m_error = INVALID_ENUM;
+            if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                         << ":INVALID_ENUM Invalid format supplied "
+                                         << glEnumToString(format);
+            return;
+        }
+
+        uchar * rawPixels = static_cast<CanvasUint8Array *>(pixels)->rawDataCptr();
         glTexImage2D(target, level, internalformat, width, height, border, format, type,
-                     static_cast<CanvasUint8Array *>(pixels)->rawDataCptr());
+                     unpackPixels(rawPixels, true,
+                                  bytesPerPixel,
+                                  width, height));
+    }
         break;
     case UNSIGNED_SHORT_4_4_4_4:
     case UNSIGNED_SHORT_5_6_5:
-    case UNSIGNED_SHORT_5_5_5_1:
+    case UNSIGNED_SHORT_5_5_5_1: {
         if (className != "CanvasUint16Array") {
-            if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION Invalid TypedArray supplied, expected Uint16Array, received " << pixels->metaObject()->className();
+            if (m_logAllErrors) qDebug() << "Context3D::"
+                                         << __FUNCTION__
+                                         << ":INVALID_OPERATION Invalid TypedArray supplied, expected Uint16Array, received "
+                                         << pixels->metaObject()->className();
             m_error = INVALID_OPERATION;
             return;
         }
+        uchar * rawPixels = static_cast<CanvasUint16Array *>(pixels)->rawDataCptr();
         glTexImage2D(target, level, internalformat, width, height, border, format, type,
-                     static_cast<CanvasUint16Array *>(pixels)->rawDataCptr());
+                     unpackPixels(rawPixels, true,
+                                  2, width, height));
+    }
         break;
     default:
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM Invalid type enum";
+        if (m_logAllErrors) qDebug() << "Context3D::"
+                                     << __FUNCTION__
+                                     << ":INVALID_ENUM Invalid type enum";
         m_error = INVALID_ENUM;
         break;
     }
@@ -789,53 +886,104 @@ void CanvasContext::texSubImage2D(glEnums target, int level,
                                   glEnums format, glEnums type,
                                   CanvasTypedArray *pixels)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "( target:" << glEnumToString(target) <<
-                                   ", level:" << level <<
-                                   ", xoffset:" << xoffset <<
-                                   ", yoffset:" << yoffset <<
-                                   ", width:" << width <<
-                                   ", height:" << height <<
-                                   ", format:" << glEnumToString(format) <<
-                                   ", type:" << glEnumToString(type) <<
-                                   ", pixels:" << pixels << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "( target:" << glEnumToString(target)
+                                << ", level:" << level
+                                << ", xoffset:" << xoffset
+                                << ", yoffset:" << yoffset
+                                << ", width:" << width
+                                << ", height:" << height
+                                << ", format:" << glEnumToString(format)
+                                << ", type:" << glEnumToString(type)
+                                << ", pixels:" << pixels << ")";
     if (!m_currentTexture) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION No current texture bound";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION No current texture bound";
         m_error = INVALID_OPERATION;
         return;
     } else if (!m_currentTexture->isAlive()) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION Currently bound texture is already deleted";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION Currently bound texture is already deleted";
         m_error = INVALID_OPERATION;
         return;
     }
 
     if (!pixels) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_VALUE pixels was null";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_VALUE pixels was null";
         m_error = INVALID_VALUE;
         return;
     }
 
     QString className(pixels->metaObject()->className());
 
+    bool deleteTempPixels = false;
+    uchar *rawData = 0;
+    int bytesPerPixel = 0;
     switch (type) {
-    case UNSIGNED_BYTE:
+    case UNSIGNED_BYTE: {
         if (className != "CanvasUint8Array") {
-            if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION Invalid TypedArray supplied, expected Uint8Array, received " << pixels->metaObject()->className();
+            if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                         << ":INVALID_OPERATION Invalid TypedArray supplied, expected Uint8Array, received "
+                                         << pixels->metaObject()->className();
             m_error = INVALID_OPERATION;
             return;
         }
-        glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
-                        static_cast<CanvasUint8Array *>(pixels)->rawDataCptr());
+        switch (format) {
+        case ALPHA:
+            bytesPerPixel = 1;
+            break;
+        case RGB:
+            bytesPerPixel = 3;
+            break;
+        case RGBA:
+            bytesPerPixel = 4;
+            break;
+        case LUMINANCE:
+            bytesPerPixel = 1;
+            break;
+        case LUMINANCE_ALPHA:
+            bytesPerPixel = 2;
+            break;
+        default:
+            break;
+        }
+
+        if (bytesPerPixel == 0) {
+            m_error = INVALID_ENUM;
+            if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                         << ":INVALID_ENUM Invalid format supplied "
+                                         << glEnumToString(format);
+            return;
+        }
+
+
+        rawData = unpackPixels(static_cast<CanvasUint8Array *>(pixels)->rawDataCptr(), true,
+                               bytesPerPixel, width, height);
+        if (rawData != static_cast<CanvasUint8Array *>(pixels)->rawDataCptr())
+            deleteTempPixels = true;
+
+        glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, rawData);
+    }
         break;
     case UNSIGNED_SHORT_4_4_4_4:
     case UNSIGNED_SHORT_5_6_5:
-    case UNSIGNED_SHORT_5_5_5_1:
+    case UNSIGNED_SHORT_5_5_5_1: {
         if (className != "CanvasUint16Array") {
-            if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION Invalid TypedArray supplied, expected Uint16Array, received " << pixels->metaObject()->className();
+            if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ <<
+                                            ":INVALID_OPERATION Invalid TypedArray supplied, expected Uint16Array, received " <<
+                                            pixels->metaObject()->className();
+
             m_error = INVALID_OPERATION;
             return;
         }
-        glTexImage2D(target, level, xoffset, yoffset, width, height, format, type,
-                     static_cast<CanvasUint16Array *>(pixels)->rawDataCptr());
+        rawData = unpackPixels(static_cast<CanvasUint16Array *>(pixels)->rawDataCptr(), true,
+                               2, width, height);
+        if (rawData != static_cast<CanvasUint16Array *>(pixels)->rawDataCptr())
+            deleteTempPixels = true;
+
+        glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, rawData);
+    }
         break;
     default:
         if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM Invalid type enum";
@@ -844,6 +992,51 @@ void CanvasContext::texSubImage2D(glEnums target, int level,
     }
 }
 
+/*!
+ * \internal
+ */
+uchar* CanvasContext::unpackPixels(uchar *srcData, bool useSrcDataAsDst,
+                                   int bytesPerPixel, int width, int height)
+{
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "( srcData:" << srcData <<
+                                   ", useSrcDataAsDst:" << useSrcDataAsDst <<
+                                   ", bytesPerPixel:" << bytesPerPixel <<
+                                   ", width:" << width <<
+                                   ", height:" << height << ")";
+
+    // Check if no processing is needed
+    if (!m_unpackFlipYEnabled || srcData == 0 || width == 0 || height == 0 || bytesPerPixel == 0)
+        return srcData;
+
+    uchar *dstData = srcData;
+    int bytesPerRow = width * bytesPerPixel;
+    if (m_unpackFlipYEnabled) {
+        if (useSrcDataAsDst) {
+            uchar *row = new uchar[width*bytesPerPixel];
+            for (int y = 0; y < height; y++) {
+                memcpy(row,
+                       srcData + y * bytesPerRow,
+                       bytesPerRow);
+                memcpy(srcData + y * bytesPerRow,
+                       srcData + (height - y - 1) * bytesPerRow,
+                       bytesPerRow);
+                memcpy(srcData + (height - y - 1) * bytesPerRow,
+                       row,
+                       bytesPerRow);
+            }
+        } else {
+            dstData = new uchar[height * bytesPerRow];
+            for (int y = 0; y < height; y++) {
+                memcpy(dstData + (height - y - 1) * bytesPerRow,
+                       srcData + y * bytesPerRow,
+                       bytesPerRow);
+            }
+        }
+    }
+
+    return dstData;
+}
+
 /*!
  * \qmlmethod void Context3D::texImage2D(glEnums target, int level, glEnums internalformat, glEnums format, glEnums type, TextureImage image)
  * Uploads the given TextureImage element to the currently bound Texture3D.
@@ -886,13 +1079,15 @@ void CanvasContext::texImage2D(glEnums target, int level, glEnums internalformat
         return;
     }
 
-    void *bits;
+    uchar *pixels = 0;
     switch (type) {
     case UNSIGNED_BYTE:
+        pixels = image->convertToFormat(type, m_unpackFlipYEnabled);
+        break;
     case UNSIGNED_SHORT_5_6_5:
     case UNSIGNED_SHORT_4_4_4_4:
     case UNSIGNED_SHORT_5_5_5_1:
-        bits = image->convertToFormat(type);
+        pixels = image->convertToFormat(type, m_unpackFlipYEnabled);
         break;
     default:
         if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM Invalid type enum";
@@ -900,13 +1095,13 @@ void CanvasContext::texImage2D(glEnums target, int level, glEnums internalformat
         return;
     }
 
-    if (bits == 0) {
+    if (pixels == 0) {
         if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":Conversion of pixels to format failed.";
         return;
     }
 
     glTexImage2D(target, level, internalformat, image->width(), image->height(), 0, format, type,
-                 bits);
+                 pixels);
 }
 
 /*!
@@ -959,13 +1154,15 @@ void CanvasContext::texSubImage2D(glEnums target, int level,
         return;
     }
 
-    void *bits;
+    uchar *pixels = 0;
     switch (type) {
     case UNSIGNED_BYTE:
+        pixels = image->convertToFormat(type, m_unpackFlipYEnabled);
+        break;
     case UNSIGNED_SHORT_5_6_5:
     case UNSIGNED_SHORT_4_4_4_4:
     case UNSIGNED_SHORT_5_5_5_1:
-        bits = image->convertToFormat(type);
+        pixels = image->convertToFormat(type, m_unpackFlipYEnabled);
         break;
     default:
         if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM Invalid type enum";
@@ -973,13 +1170,13 @@ void CanvasContext::texSubImage2D(glEnums target, int level,
         return;
     }
 
-    if (bits == 0) {
+    if (pixels == 0) {
         if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":Conversion of pixels to format failed.";
         return;
     }
 
     glTexSubImage2D(target, level, xoffset, yoffset, image->width(), image->height(), format,
-                    type, bits);
+                    type, pixels);
 }
 
 /*!
@@ -1593,13 +1790,13 @@ void CanvasContext::pixelStorei(glEnums pname, int param)
 
     switch (pname) {
     case UNPACK_FLIP_Y_WEBGL:
-        qDebug() << "Context3D::" << __FUNCTION__ << "(pname:" << glEnumToString(pname) << " param:" << param << ") NOT IMPLEMENTED!";
+        m_unpackFlipYEnabled = (param != 0);
         break;
     case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
-        qDebug() << "Context3D::" << __FUNCTION__ << "(pname:" << glEnumToString(pname) << " param:" << param << ") NOT IMPLEMENTED!";
+        m_unpackPremultiplyAlphaEnabled = (param != 0);
         break;
     case UNPACK_COLORSPACE_CONVERSION_WEBGL:
-        qDebug() << "Context3D::" << __FUNCTION__ << "(pname:" << glEnumToString(pname) << " param:" << param << ") NOT IMPLEMENTED!";
+        m_unpackColorspaceConversion = glEnums(param);
         break;
     default:
         glPixelStorei(GLenum(pname), param);
@@ -1845,7 +2042,8 @@ QVariant CanvasContext::getProgramParameter(CanvasProgram *program, glEnums para
     default: {
         // TODO: Implement rest of the parameters
         m_error = INVALID_ENUM;
-        qDebug() << "Context3D::" << __FUNCTION__ << ": INVALID_ENUM illegal parameter name ";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ": INVALID_ENUM illegal parameter name ";
         return QVariant::fromValue(0);
     }
     }
@@ -2522,7 +2720,8 @@ int CanvasContext::getShaderParameter(CanvasShader *shader, glEnums pname)
         return (isCompiled ? GL_TRUE : GL_FALSE);
     }
     default: {
-        qDebug() << "getShaderParameter() : UNSUPPORTED parameter name " << glEnumToString(pname);
+        if (m_logAllErrors) qDebug() << "getShaderParameter() : UNSUPPORTED parameter name "
+                                     << glEnumToString(pname);
         return 0;
     }
     }
@@ -2952,16 +3151,21 @@ void CanvasContext::bufferData(glEnums target, CanvasArrayBuffer &data, glEnums
  */
 void CanvasContext::bufferSubData(glEnums target, int offset, CanvasTypedArray *typedArray)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(target:" << glEnumToString(target) << ", offset:"<< offset << ", typedArray:" << typedArray << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(target:" << glEnumToString(target)
+                                << ", offset:"<< offset
+                                << ", typedArray:" << typedArray << ")";
 
     if (target != ARRAY_BUFFER && target != ELEMENT_ARRAY_BUFFER) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM target must be either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_ENUM target must be either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.";
         m_error = INVALID_ENUM;
         return;
     }
 
     if (!typedArray) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ": INVALID_VALUE called with null data";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ": INVALID_VALUE called with null data";
         m_error = INVALID_VALUE;
         return;
     }
@@ -2980,10 +3184,14 @@ void CanvasContext::bufferSubData(glEnums target, int offset, CanvasTypedArray *
  */
 void CanvasContext::bufferSubData(glEnums target, int offset, CanvasArrayBuffer &data)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(target:" << glEnumToString(target) << ", offset:"<< offset << ", data.byteLength:" << data.byteLength() << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(target:" << glEnumToString(target)
+                                << ", offset:"<< offset
+                                << ", data.byteLength:" << data.byteLength() << ")";
 
     if (target != ARRAY_BUFFER && target != ELEMENT_ARRAY_BUFFER) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM target must be either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_ENUM target must be either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.";
         m_error = INVALID_ENUM;
         return;
     }
@@ -3002,10 +3210,13 @@ void CanvasContext::bufferSubData(glEnums target, int offset, CanvasArrayBuffer
  */
 QVariant CanvasContext::getBufferParameter(glEnums target, glEnums pname)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(target:" << glEnumToString(target) << ", pname" << glEnumToString(pname) << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(target:" << glEnumToString(target)
+                                << ", pname" << glEnumToString(pname) << ")";
 
     if (target != ARRAY_BUFFER && target != ELEMENT_ARRAY_BUFFER) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM target must be either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_ENUM target must be either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.";
         m_error = INVALID_ENUM;
         return 0;
     }
@@ -3036,7 +3247,8 @@ QVariant CanvasContext::getBufferParameter(glEnums target, glEnums pname)
  */
 bool CanvasContext::isBuffer(QObject *anyObject)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "( anyObject:" << anyObject << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "( anyObject:" << anyObject << ")";
     if (!anyObject)
         return false;
 
@@ -3060,9 +3272,11 @@ bool CanvasContext::isBuffer(QObject *anyObject)
  */
 void CanvasContext::deleteBuffer(CanvasBuffer *buffer)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(buffer:" << buffer << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(buffer:" << buffer << ")";
     if (!buffer) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ": Called with null buffer target";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ": Called with null buffer target";
         return;
     }
 
@@ -3163,7 +3377,8 @@ QVariant CanvasContext::getParameter(glEnums pname)
         return qtext;
     }
     default: {
-        qDebug() << "Context3D::" << __FUNCTION__ << "(): UNIMPLEMENTED PARAMETER NAME" << glEnumToString(pname);
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << "(): UNIMPLEMENTED PARAMETER NAME" << glEnumToString(pname);
         return QVariant::fromValue(0);
     }
     }
@@ -3181,7 +3396,8 @@ QVariant CanvasContext::getParameter(glEnums pname)
  */
 QString CanvasContext::getShaderInfoLog(CanvasShader *shader) const
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "( shader:" << shader<< ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "( shader:" << shader<< ")";
     if (!shader)
         return QString();
 
@@ -3198,7 +3414,8 @@ QString CanvasContext::getShaderInfoLog(CanvasShader *shader) const
  */
 QString CanvasContext::getProgramInfoLog(CanvasProgram *program) const
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "( program:" << program<< ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "( program:" << program<< ")";
     if (!program)
         return QString();
 
@@ -3217,7 +3434,9 @@ QString CanvasContext::getProgramInfoLog(CanvasProgram *program) const
  */
 void CanvasContext::bindBuffer(glEnums target, CanvasBuffer *buffer)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "( target:" << glEnumToString(target) << ", buffer:" <<buffer<< ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "( target:" << glEnumToString(target)
+                                << ", buffer:" <<buffer<< ")";
 
     if (target != ARRAY_BUFFER && target != ELEMENT_ARRAY_BUFFER) {
         if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM target must be either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.";
@@ -3264,7 +3483,8 @@ void CanvasContext::bindBuffer(glEnums target, CanvasBuffer *buffer)
  */
 void CanvasContext::validateProgram(CanvasProgram *program)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(program:" << program << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(program:" << program << ")";
     if (program)
         program->validateProgram();
 }
@@ -3278,7 +3498,8 @@ void CanvasContext::validateProgram(CanvasProgram *program)
  */
 void CanvasContext::useProgram(CanvasProgram *program)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(program:" << program << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(program:" << program << ")";
     // TODO: Check if this is ok
     m_currentProgram = program;
     if (!program || !program->qOGLProgram()->isLinked())
@@ -3295,7 +3516,8 @@ void CanvasContext::useProgram(CanvasProgram *program)
  */
 void CanvasContext::clear(glEnums flags)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(flags:" << glEnumToString(flags) << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(flags:" << glEnumToString(flags) << ")";
     glClear(flags);
 }
 
@@ -3309,7 +3531,8 @@ void CanvasContext::clear(glEnums flags)
  */
 void CanvasContext::cullFace(glEnums mode)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(mode:" << glEnumToString(mode) << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(mode:" << glEnumToString(mode) << ")";
     glCullFace(mode);
 }
 
@@ -3323,7 +3546,8 @@ void CanvasContext::cullFace(glEnums mode)
  */
 void CanvasContext::frontFace(glEnums mode)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(mode:" << glEnumToString(mode) << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(mode:" << glEnumToString(mode) << ")";
     glFrontFace(mode);
 }
 
@@ -3336,7 +3560,8 @@ void CanvasContext::frontFace(glEnums mode)
  */
 void CanvasContext::depthMask(bool flag)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(flag:" << flag << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(flag:" << flag << ")";
     if (flag)
         glDepthMask(GL_TRUE);
     else
@@ -3355,7 +3580,8 @@ void CanvasContext::depthMask(bool flag)
  */
 void CanvasContext::depthFunc(glEnums func)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(func:" << glEnumToString(func) << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(func:" << glEnumToString(func) << ")";
     glDepthFunc(func);
 }
 
@@ -3369,7 +3595,9 @@ void CanvasContext::depthFunc(glEnums func)
  */
 void CanvasContext::depthRange(float zNear, float zFar)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(zNear:" << zNear << ", zFar:" << zFar <<  ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(zNear:" << zNear
+                                << ", zFar:" << zFar <<  ")";
     glDepthRangef(GLclampf(zNear), GLclampf(zFar));
 }
 
@@ -3396,7 +3624,11 @@ void CanvasContext::clearStencil(int stencil)
  */
 void CanvasContext::colorMask(bool maskRed, bool maskGreen, bool maskBlue, bool maskAlpha)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(maskRed:" << maskRed << ", maskGreen:" << maskGreen << ", maskBlue:" << maskBlue  << ", maskAlpha:" << maskAlpha  <<  ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(maskRed:" << maskRed
+                                << ", maskGreen:" << maskGreen
+                                << ", maskBlue:" << maskBlue
+                                << ", maskAlpha:" << maskAlpha  <<  ")";
     glColorMask(maskRed, maskGreen, maskBlue, maskAlpha);
 }
 
@@ -3424,7 +3656,11 @@ void CanvasContext::clearDepth(float depth)
  */
 void CanvasContext::clearColor(float red, float green, float blue, float alpha)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ <<  "(red:" << red << ", green:" << green << ", blue:" << blue << ", alpha:" << alpha << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                <<  "(red:" << red
+                                 << ", green:" << green
+                                 << ", blue:" << blue
+                                 << ", alpha:" << alpha << ")";
     glClearColor(red, green, blue, alpha);
 }
 
@@ -3442,7 +3678,11 @@ void CanvasContext::clearColor(float red, float green, float blue, float alpha)
  */
 void CanvasContext::viewport(int x, int y, int width, int height)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ <<  "(x:" << x << ", y:" << y << ", width:" << width << ", height:" << height << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                <<  "(x:" << x
+                                 << ", y:" << y
+                                 << ", width:" << width
+                                 << ", height:" << height << ")";
     glViewport(x, y, width, height);
     m_glViewportRect.setX(x);
     m_glViewportRect.setY(y);
@@ -3462,7 +3702,10 @@ void CanvasContext::viewport(int x, int y, int width, int height)
  */
 void CanvasContext::drawArrays(glEnums mode, int first, int count)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(mode:" << glEnumToString(mode) << ", first:" << first << ", count:" << count << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(mode:" << glEnumToString(mode)
+                                << ", first:" << first
+                                << ", count:" << count << ")";
     glDrawArrays(mode, first, count);
 }
 
@@ -3480,7 +3723,11 @@ void CanvasContext::drawArrays(glEnums mode, int first, int count)
  */
 void CanvasContext::drawElements(glEnums mode, int count, glEnums type, long offset)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(mode:" << glEnumToString(mode) << ", count:" << count << ", type:" << glEnumToString(type) << ", offset:" << offset << ")";
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(mode:" << glEnumToString(mode)
+                                << ", count:" << count
+                                << ", type:" << glEnumToString(type)
+                                << ", offset:" << offset << ")";
     glDrawElements(GLenum(mode), count, GLenum(type), (GLvoid*)offset);
 }
 
@@ -3497,26 +3744,78 @@ void CanvasContext::readPixels(int x, int y, long width, long height, glEnums fo
                                CanvasArrayBufferView *pixels)
 {
     if (format != RGBA) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM format must be RGBA.";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_ENUM format must be RGBA.";
         m_error = INVALID_ENUM;
         return;
     }
     if (type != UNSIGNED_BYTE) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_ENUM type must be UNSIGNED_BYTE.";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_ENUM type must be UNSIGNED_BYTE.";
         m_error = INVALID_ENUM;
         return;
     }
     if (!pixels) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_VALUE pixels was null.";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_VALUE pixels was null.";
         m_error = INVALID_VALUE;
         return;
     }
 
     if (pixels->bufferType() != CanvasTypedArray::ARRAY_BUFFER_UINT_8) {
-        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ":INVALID_OPERATION pixels must be Uint8Array.";
+        if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__
+                                     << ":INVALID_OPERATION pixels must be Uint8Array.";
         m_error = INVALID_OPERATION;
         return;
     }
 
     glReadPixels(x, y, width, height, format, type, pixels->rawDataCptr());
 }
+
+/*!
+ * \qmlmethod ActiveInfo3D Context3D::getActiveAttrib(Program3D program, uint index)
+ * Returns information about the given active attribute variable defined by \a index for the given
+ * \a program.
+ * \sa ActiveInfo3D
+ */
+/*!
+ * \internal
+ */
+CanvasActiveInfo *CanvasContext::getActiveAttrib(CanvasProgram *program, uint index)
+{
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(program:" << program
+                                << ", index:" << index << ")";
+
+    char *name = new char[512];
+    GLsizei length = 0;
+    int size = 0;
+    GLenum type = 0;
+    glGetActiveAttrib(program->id(), index, 512, &length, &size, &type, name);
+    QString strName(name);
+    return new CanvasActiveInfo(size, CanvasContext::glEnums(type), strName);
+}
+
+/*!
+ * \qmlmethod ActiveInfo3D Context3D::getActiveUniform(Program3D program, uint index)
+ * Returns information about the given active uniform variable defined by \a index for the given
+ * \a program.
+ * \sa ActiveInfo3D
+ */
+/*!
+ * \internal
+ */
+CanvasActiveInfo *CanvasContext::getActiveUniform(CanvasProgram *program, uint index)
+{
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(program:" << program
+                                << ", index:" << index << ")";
+
+    char *name = new char[512];
+    GLsizei length = 0;
+    int size = 0;
+    GLenum type = 0;
+    glGetActiveUniform(program->id(), index, 512, &length, &size, &type, name);
+    QString strName(name);
+    return new CanvasActiveInfo(size, CanvasContext::glEnums(type), strName);
+}
diff --git a/src/context3d_p.h b/src/context3d_p.h
index 9649cc6..7c56758 100644
--- a/src/context3d_p.h
+++ b/src/context3d_p.h
@@ -64,6 +64,7 @@
 #define ENUM_AS_PROPERTY(A) Q_PROPERTY(CanvasContext::glEnums A READ A ## _read); inline CanvasContext::glEnums A ## _read() { return CanvasContext::A; }
 
 class Canvas;
+class CanvasActiveInfo;
 class CanvasTexture;
 class CanvasShader;
 class CanvasFrameBuffer;
@@ -76,6 +77,8 @@ class CanvasArrayBufferView;
 class CanvasArrayBuffer;
 class CanvasFloat32Array;
 class CanvasInt32Array;
+class CanvasUint16Array;
+class CanvasUint8Array;
 class CanvasTypedArray;
 class CanvasShaderPrecisionFormat;
 class EnumToStringMap;
@@ -88,6 +91,9 @@ class QT_CANVAS3D_EXPORT CanvasContext : public QObject, protected QOpenGLFuncti
     Q_ENUMS(glEnums)
 
     Q_PROPERTY(Canvas *canvas READ canvas NOTIFY canvasChanged)
+    Q_PROPERTY(uint drawingBufferWidth READ drawingBufferWidth NOTIFY drawingBufferWidthChanged)
+    Q_PROPERTY(uint drawingBufferHeight READ drawingBufferHeight NOTIFY drawingBufferHeightChanged)
+
     Q_PROPERTY(bool logAllCalls READ logAllCalls WRITE setLogAllCalls NOTIFY logAllCallsChanged)
     Q_PROPERTY(bool logAllErrors READ logAllErrors WRITE setLogAllErrors NOTIFY logAllErrorsChanged)
 
@@ -938,6 +944,9 @@ public:
     void setCanvas(Canvas *canvas);
     Canvas *canvas();
 
+    uint drawingBufferWidth();
+    uint drawingBufferHeight();
+
     Q_INVOKABLE QVariantList getSupportedExtensions();
     Q_INVOKABLE QVariant getExtension(const QString &name);
 
@@ -1142,6 +1151,10 @@ public:
     Q_INVOKABLE void readPixels(int x, int y, long width, long height, glEnums format,
                                 glEnums type, CanvasArrayBufferView *pixels);
 
+    Q_INVOKABLE CanvasActiveInfo *getActiveAttrib(CanvasProgram *program, uint index);
+    Q_INVOKABLE CanvasActiveInfo *getActiveUniform(CanvasProgram *program, uint index);
+
+
     QString glEnumToString(glEnums value) const;
     float devicePixelRatio();
     void setDevicePixelRatio(float ratio);
@@ -1151,11 +1164,37 @@ public:
     QRect glViewportRect() const;
     GLuint currentFramebuffer();
 
+    /*
+    TODO: Add these missing functions
+    sequence<WebGLShader>? getAttachedShaders(WebGLProgram program);
+    any getFramebufferAttachmentParameter(GLenum target, GLenum attachment, GLenum pname);
+    any getRenderbufferParameter(GLenum target, GLenum pname);
+    any getTexParameter(GLenum target, GLenum pname);
+    any getUniform(WebGLProgram program, WebGLUniformLocation? location);
+    any getVertexAttrib(GLuint index, GLenum pname);
+    GLsizeiptr getVertexAttribOffset(GLuint index, GLenum pname);
+
+    void stencilFunc(GLenum func, GLint ref, GLuint mask);
+    void stencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
+    void stencilMask(GLuint mask);
+    void stencilMaskSeparate(GLenum face, GLuint mask);
+    void stencilOp(GLenum fail, GLenum zfail, GLenum zpass);
+    void stencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
+
+     void vertexAttrib1fv(GLuint indx, sequence<GLfloat> values);
+     void vertexAttrib2fv(GLuint indx, sequence<GLfloat> values);
+     void vertexAttrib3fv(GLuint indx, sequence<GLfloat> values);
+     void vertexAttrib4fv(GLuint indx, sequence<GLfloat> values);
+    */
+
     void setLogAllCalls(bool logCalls);
     bool logAllCalls() const;
     void setLogAllErrors(bool logErrors);
     bool logAllErrors() const;
 
+    uchar *unpackPixels(uchar *srcData, bool useSrcDataAsDst,
+                        int bytesPerPixel, int width, int height);
+
 signals:
     //void viewportChanged(const QRect &viewport); // TODO: unused
     //void viewportWidthChanged(int width); // TODO: unused
@@ -1163,9 +1202,15 @@ signals:
     void canvasChanged(Canvas *canvas);
     void logAllCallsChanged(bool logCalls);
     void logAllErrorsChanged(bool logErrors);
+    void drawingBufferWidthChanged();
+    void drawingBufferHeightChanged();
 
 private:
 
+    bool m_unpackFlipYEnabled;
+    bool m_unpackPremultiplyAlphaEnabled;
+    glEnums m_unpackColorspaceConversion;
+
     bool m_logAllCalls;
     bool m_logAllErrors;
     QRect m_glViewportRect;
diff --git a/src/qcanvas3d_plugin.cpp b/src/qcanvas3d_plugin.cpp
index c5ece02..3c1cf1b 100644
--- a/src/qcanvas3d_plugin.cpp
+++ b/src/qcanvas3d_plugin.cpp
@@ -115,6 +115,10 @@ void QtCanvas3DPlugin::registerTypes(const char *uri)
                                               1, 0,
                                               "Context3D",
                                               QLatin1String("Trying to create uncreatable: Context3D, use Canvas3D.getContext() instead."));
+    qmlRegisterUncreatableType<CanvasActiveInfo>(uri,
+                                                 1, 0,
+                                                 "ActiveInfo3D",
+                                                 QLatin1String("Trying to create uncreatable: ActiveInfo3D, use Context3D.getActiveAttrib() or Context3D.getActiveUniform() instead."));
     qmlRegisterUncreatableType<CanvasTexture>(uri,
                                               1, 0,
                                               "Texture3D",
diff --git a/src/qcanvas3d_plugin.h b/src/qcanvas3d_plugin.h
index d1ad566..7b7f43d 100644
--- a/src/qcanvas3d_plugin.h
+++ b/src/qcanvas3d_plugin.h
@@ -63,6 +63,7 @@
 #include "renderbuffer3d_p.h"
 #include "shaderprecisionformat_p.h"
 #include "teximage3dloader_p.h"
+#include "activeinfo3d_p.h"
 
 #include <QQmlExtensionPlugin>
 
@@ -93,6 +94,7 @@ QML_DECLARE_TYPE(CanvasFrameBuffer)
 QML_DECLARE_TYPE(CanvasRenderBuffer)
 QML_DECLARE_TYPE(CanvasShaderPrecisionFormat)
 QML_DECLARE_TYPE(CanvasTextureImageLoader)
+QML_DECLARE_TYPE(CanvasActiveInfo)
 
 class QtCanvas3DPlugin : public QQmlExtensionPlugin
 {
diff --git a/src/src.pro b/src/src.pro
index 632e43b..b93e42f 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -43,7 +43,8 @@ SOURCES += qcanvas3d_plugin.cpp \
     teximage3d.cpp \
     teximage3dloader.cpp \
     texture3d.cpp \
-    uniformlocation.cpp
+    uniformlocation.cpp \
+    activeinfo3d.cpp
 
 HEADERS += qcanvas3d_plugin.h \
     arraybuffer_p.h \
@@ -77,7 +78,8 @@ HEADERS += qcanvas3d_plugin.h \
     teximage3d_p.h \
     teximage3dloader_p.h \
     texture3d_p.h \
-    uniformlocation_p.h
+    uniformlocation_p.h \
+    activeinfo3d_p.h
 
 OTHER_FILES = qmldir \
     doc/* \
diff --git a/src/teximage3d.cpp b/src/teximage3d.cpp
index feaa0cd..7ab10fa 100644
--- a/src/teximage3d.cpp
+++ b/src/teximage3d.cpp
@@ -60,7 +60,8 @@ CanvasTextureImage::CanvasTextureImage(QObject *parent) :
     m_state(INITIALIZED),
     m_errorString(""),
     m_pixelCache(0),
-    m_pixelCacheFormat(CanvasContext::NONE)
+    m_pixelCacheFormat(CanvasContext::NONE),
+    m_pixelCacheFlipY(false)
 {
     m_networkAccessManager = new QNetworkAccessManager(this);
     QObject::connect(m_networkAccessManager, &QNetworkAccessManager::finished,
@@ -160,8 +161,6 @@ void CanvasTextureImage::handleReply(QNetworkReply *reply)
     }
 
     m_image.loadFromData(reply->readAll());
-    m_image = m_image.mirrored(false, true);
-    m_glImage = m_image.convertToFormat(QImage::Format_RGBA8888);
 
     setImageState(LOADING_FINISHED);
 }
@@ -174,14 +173,6 @@ QImage & CanvasTextureImage::getImage()
     return m_image;
 }
 
-/*!
- * \internal
- */
-uchar *CanvasTextureImage::getImageData()
-{
-    return m_image.bits();
-}
-
 /*!
  * \internal
  */
@@ -270,27 +261,39 @@ void CanvasTextureImage::emitImageLoadingErrorSGRT()
 {
 }
 
+
 /*!
  * \internal
  */
-void *CanvasTextureImage::convertToFormat(CanvasContext::glEnums format)
+uchar *CanvasTextureImage::convertToFormat(CanvasContext::glEnums format, bool flipY)
 {
-    if (m_pixelCacheFormat == format)
+    if (m_pixelCacheFormat == format && m_pixelCacheFlipY == flipY)
         return m_pixelCache;
 
+    // Destroy the pixel cache
+    delete m_pixelCache;
+    m_pixelCache = 0;
+    m_pixelCacheFormat = CanvasContext::NONE;
+
+    // Flip the image if needed
+    if (m_pixelCacheFlipY != flipY) {
+        m_image = m_image.mirrored(false, true);
+        m_glImage = m_image.convertToFormat(QImage::Format_RGBA8888);
+        m_pixelCacheFlipY = flipY;
+    }
+
+    // Get latest data for the conversion
     uchar *origPixels = m_glImage.bits();
     int width = m_glImage.width();
     int height = m_glImage.height();
 
+    // Handle format conversions if needed
     switch (format) {
     case CanvasContext::UNSIGNED_BYTE: {
         return origPixels;
         break;
     }
     case CanvasContext::UNSIGNED_SHORT_5_6_5: {
-        delete m_pixelCache;
-        m_pixelCache = 0;
-        m_pixelCacheFormat = CanvasContext::NONE;
         ushort *pixels = new ushort[width * height];
         ushort pixel;
         for (int y = 0; y < height; y++) {
@@ -307,9 +310,6 @@ void *CanvasTextureImage::convertToFormat(CanvasContext::glEnums format)
         return m_pixelCache;
     }
     case CanvasContext::UNSIGNED_SHORT_4_4_4_4: {
-        delete m_pixelCache;
-        m_pixelCache = 0;
-        m_pixelCacheFormat = CanvasContext::NONE;
         ushort *pixels = new ushort[width * height];
         ushort pixel;
         for (int y = 0; y < height; y++) {
@@ -327,9 +327,6 @@ void *CanvasTextureImage::convertToFormat(CanvasContext::glEnums format)
         return m_pixelCache;
     }
     case CanvasContext::UNSIGNED_SHORT_5_5_5_1: {
-        delete m_pixelCache;
-        m_pixelCache = 0;
-        m_pixelCacheFormat = CanvasContext::NONE;
         ushort *pixels = new ushort[width * height];
         ushort pixel;
         for (int y = 0; y < height; y++) {
diff --git a/src/teximage3d_p.h b/src/teximage3d_p.h
index f6f2d64..c2824c3 100644
--- a/src/teximage3d_p.h
+++ b/src/teximage3d_p.h
@@ -93,8 +93,7 @@ public:
     void load();
     void handleReply(QNetworkReply *reply);
     QImage &getImage();
-    uchar *getImageData();
-    void *convertToFormat(CanvasContext::glEnums format);
+    uchar *convertToFormat(CanvasContext::glEnums format, bool flipY = false);
 
     void emitImageLoadedSGRT();
     void emitImageLoadingErrorSGRT();
@@ -121,6 +120,7 @@ private:
     QString m_errorString;
     uchar *m_pixelCache;
     CanvasContext::glEnums m_pixelCacheFormat;
+    bool m_pixelCacheFlipY;
     QImage m_glImage;
     QVariant *m_anyValue;
 };
-- 
GitLab