summaryrefslogtreecommitdiffstats
path: root/src/threed/textures
diff options
context:
space:
mode:
Diffstat (limited to 'src/threed/textures')
-rw-r--r--src/threed/textures/qareaallocator.cpp877
-rw-r--r--src/threed/textures/qareaallocator.h169
-rw-r--r--src/threed/textures/qglsharedresource.cpp236
-rw-r--r--src/threed/textures/qglsharedresource_p.h96
-rw-r--r--src/threed/textures/qgltexture2d.cpp697
-rw-r--r--src/threed/textures/qgltexture2d.h110
-rw-r--r--src/threed/textures/qgltexture2d_p.h116
-rw-r--r--src/threed/textures/qgltexturecube.cpp549
-rw-r--r--src/threed/textures/qgltexturecube.h112
-rw-r--r--src/threed/textures/qgltextureutils.cpp785
-rw-r--r--src/threed/textures/qgltextureutils_p.h164
-rw-r--r--src/threed/textures/textures.pri15
12 files changed, 3926 insertions, 0 deletions
diff --git a/src/threed/textures/qareaallocator.cpp b/src/threed/textures/qareaallocator.cpp
new file mode 100644
index 000000000..d6d92de88
--- /dev/null
+++ b/src/threed/textures/qareaallocator.cpp
@@ -0,0 +1,877 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qareaallocator.h"
+#include "qglnamespace.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAreaAllocator
+ \brief The QAreaAllocator class provides facilities for allocating sub-regions from a rectangular image.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::textures
+ \internal
+
+ Performance on a system can sometimes be improved by storing
+ multiple small images in a single large image. This reduces
+ memory allocation overhead and GPU state switching costs.
+
+ QAreaAllocator and its subclasses implement standard strategies
+ for sub-region allocation in images without tying those strategies
+ to specific technologies such as raster, OpenGL, etc.
+
+ Allocations are managed in a virtual two-dimensional space.
+ The caller performs the actual texture upload based on the sub-region
+ that allocate() returns.
+
+ The caller can return a sub-region to the allocation pool with
+ release(). Note that not all strategies support release().
+
+ \sa QSimpleAreaAllocator, QGeneralAreaAllocator, QUniformAreaAllocator
+*/
+
+/*!
+ \class QSimpleAreaAllocator
+ \brief The QSimpleAreaAllocator class provides a simple allocation policy for simple-sized sub-allocations.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::textures
+ \internal
+
+ QSimpleAreaAllocator uses a trivial allocation strategy whereby
+ sub-regions are allocated in rows, with a new row started each
+ time the previous row fills up. Space is never reclaimed by
+ release().
+
+ This allocator is suitable for use when the allocations will all
+ be of a similar size and all regions will be discarded at
+ the same time when the allocator is destroyed. An example would
+ be a font glyph manager.
+
+ \sa QAreaAllocator, QGeneralAreaAllocator
+*/
+
+/*!
+ \class QGeneralAreaAllocator
+ \brief The QGeneralAreaAllocator class provides a general allocation policy for sub-regions in an image.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::textures
+ \internal
+
+ QGeneralAreaAllocator can handle arbitrary-sized allocations up to
+ size(), in any order, and can release() previously allocated regions.
+ It uses a binary subdivision algorithm on the image, which may result
+ in fragmentation under heavy load.
+
+ While technically any size sub-region up to size() can be allocated,
+ once subdivision begins, the sizes that can be allocated will reduce
+ substantially. It is recommended that incoming requests be size() / 4
+ or less for best performance.
+
+ If the sub-region sizes to be allocated are very similar, and release()
+ is not necessary, then QSimpleAreaAllocator may work better than
+ QGeneralAreaAllocator. If the sizes are very similar, and
+ release() is necessary, then QUniformAreaAllocator may work better
+ than QGeneralAreaAllocator.
+
+ \sa QAreaAllocator, QSimpleAreaAllocator, QUniformAreaAllocator
+*/
+
+/*!
+ \class QUniformAreaAllocator
+ \brief The QUniformAreaAllocator class provides an allocation policy for uniform-sized areas.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::textures
+ \internal
+
+ QUniformAreaAllocator allocates any size up to uniformSize()
+ by dividing size() up into a grid of uniformSize() areas.
+ Areas can be deallocated with release() and returned to the pool.
+
+ This allocator is suitable for use when the allocations will all
+ be of a similar size. Unlike QSimpleAreaAllocator, this class
+ can release allocations.
+
+ \sa QAreaAllocator, QSimpleAreaAllocator, QGeneralAreaAllocator
+*/
+
+/*!
+ \internal
+
+ Constructs a new area allocator that is initially \a size pixels
+ in size.
+
+ \sa expand()
+*/
+QAreaAllocator::QAreaAllocator(const QSize &size)
+ : m_size(size)
+ , m_minAlloc(1, 1)
+ , m_margin(0, 0)
+{
+}
+
+/*!
+ \internal
+
+ Destroys this area allocator.
+*/
+QAreaAllocator::~QAreaAllocator()
+{
+}
+
+/*!
+ \fn QSize QAreaAllocator::size() const
+ \internal
+
+ Returns the current size of the area being used by this allocator.
+*/
+
+/*!
+ \fn QSize QAreaAllocator::minimumAllocation() const
+ \internal
+
+ Returns the minimum allocation size in the x and y directions
+ for areas returned by allocate(). The default is (1, 1).
+
+ \sa setMinimumAllocation()
+*/
+
+/*!
+ \fn void QAreaAllocator::setMinimumAllocation(const QSize &size)
+ \internal
+
+ Sets the minimum allocation \a size in the x and y directions
+ for areas returned by allocate().
+
+ For example, setting the minimum allocation to (8, 8) will force
+ all allocations to be aligned on an 8-pixel boundary.
+
+ \sa minimumAllocation()
+*/
+
+/*!
+ \fn QSize QAreaAllocator::margin() const
+ \internal
+
+ Returns the margin that should be left between allocated items
+ in the x and y directions. The default is (0, 0).
+
+ This may be needed when using OpenGL textures because of
+ rounding errors in the floating-point representation of
+ texture co-ordinates. Leaving a small margin between allocations
+ can help avoid adjacent images from bleeding into each other.
+
+ \sa setMargin()
+*/
+
+/*!
+ \fn void QAreaAllocator::setMargin(const QSize &margin)
+ \internal
+
+ Sets the \a margin that should be left between allocated items
+ in the x and y directions.
+
+ \sa margin()
+*/
+
+/*!
+ \internal
+
+ Expands this allocator to encompass the width and height of \a size.
+ If the area is already larger, this function does nothing.
+
+ The rectangles that were returned for previous allocations will
+ remain valid.
+
+ \sa expandBy()
+*/
+void QAreaAllocator::expand(const QSize &size)
+{
+ int newWidth = qMax(m_size.width(), size.width());
+ int newHeight = qMax(m_size.height(), size.height());
+ m_size = QSize(newWidth, newHeight);
+}
+
+/*!
+ \internal
+
+ Expands this allocator by \a size pixels in the x and y directions.
+
+ For example, expanding by (0, 128) will add 128 additional pixels
+ of height but will leave the width unchanged.
+
+ \sa expand()
+*/
+void QAreaAllocator::expandBy(const QSize &size)
+{
+ expand(m_size + size);
+}
+
+/*!
+ \fn QRect QAreaAllocator::allocate(const QSize &size)
+ \internal
+
+ Allocates \a size pixels from this allocator and returns the rectangle
+ that should be used by the caller. Returns a null rectangle if
+ this allocator does not have sufficient space to accommodate \a size.
+
+ \sa release()
+*/
+
+/*!
+ \internal
+
+ Allocates and returns a list of rectangles corresponding to the
+ elements of \a sizes. The returned list will have less elements
+ than \a sizes if there is insufficient space to accommodate
+ all of the allocation requests. The values that are in the returned
+ list will be allocated and need to be passed to release() to
+ deallocate them.
+
+ The default implementation will call the subclass allocate() once
+ for each size until all \a sizes have been allocated or an
+ allocation fails. Subclasses may override this method to
+ reorder the allocations for best-fit.
+
+ \sa release()
+*/
+QList<QRect> QAreaAllocator::allocate(const QList<QSize> &sizes)
+{
+ QList<QRect> rects;
+ QRect rect;
+ for (int index = 0; index < sizes.count(); ++index) {
+ rect = allocate(sizes[index]);
+ if (rect.isNull())
+ break;
+ rects.append(rect);
+ }
+ return rects;
+}
+
+/*!
+ \internal
+
+ Releases the space occupied by \a rect back to the allocator.
+ The default implementation does nothing.
+
+ The \a rect must have been returned by a previous call to allocate().
+ Otherwise the behaviour is undefined.
+
+ \sa allocate()
+*/
+void QAreaAllocator::release(const QRect &rect)
+{
+ Q_UNUSED(rect);
+}
+
+/*!
+ \internal
+
+ Releases the space occupied by the members of \a rects back to
+ the allocator. The default implementation calls release() for
+ each rectangle in the list.
+
+ The members of \a rects must have been returned by previous calls
+ to allocate(). Otherwise the behaviour is undefined.
+
+ \sa allocate()
+*/
+void QAreaAllocator::release(const QList<QRect> &rects)
+{
+ for (int index = 0; index < rects.count(); ++index)
+ release(rects[index]);
+}
+
+/*!
+ \internal
+
+ Returns a rough estimate of the number of bytes of overhead that
+ are currently in use to store the house-keeping data structures
+ for this area allocator. The default implementation returns zero.
+*/
+int QAreaAllocator::overhead() const
+{
+ return 0;
+}
+
+/*!
+ \internal
+
+ Returns \a size, after rounding it up to account for
+ minimumAllocation() and margin().
+
+ This is a convenience function, provided for subclass overrides
+ of allocate().
+
+ \sa allocate()
+*/
+QSize QAreaAllocator::roundAllocation(const QSize &size) const
+{
+ int width = size.width() + m_margin.width();
+ int height = size.height() + m_margin.height();
+ int extra = width % m_minAlloc.width();
+ if (extra)
+ width += m_minAlloc.width() - extra;
+ extra = height % m_minAlloc.height();
+ if (extra)
+ height += m_minAlloc.height() - extra;
+ return QSize(width, height);
+}
+
+/*!
+ \internal
+
+ Constructs a simple area allocator that is initially \a size pixels
+ in size.
+*/
+QSimpleAreaAllocator::QSimpleAreaAllocator(const QSize &size)
+ : QAreaAllocator(size)
+ , m_row(0)
+ , m_column(0)
+ , m_rowHeight(0)
+{
+}
+
+/*!
+ \internal
+
+ Destroys this simple area allocator.
+*/
+QSimpleAreaAllocator::~QSimpleAreaAllocator()
+{
+}
+
+/*!
+ \internal
+*/
+QRect QSimpleAreaAllocator::allocate(const QSize &size)
+{
+ // Round up the allocation size to account for the margin and
+ // minimum allocation parameters.
+ QSize rounded = roundAllocation(size);
+ int width = rounded.width();
+ int height = rounded.height();
+
+ // Bail out if the size is obviously too small or too big.
+ if (width <= 0 || width > m_size.width())
+ return QRect();
+ if (height <= 0 || height > (m_size.height() - m_row))
+ return QRect();
+
+ // Do we need to place this allocation on a new row?
+ int row = m_row;
+ int column = m_column;
+ int rowHeight = m_rowHeight;
+ if ((column + width) > m_size.width()) {
+ row += m_rowHeight;
+ column = 0;
+ rowHeight = 0;
+ if (height > (m_size.height() - row))
+ return QRect();
+ }
+
+ // Update the current allocation position.
+ m_row = row;
+ m_column = column + width;
+ m_rowHeight = qMax(rowHeight, height);
+
+ // Return the allocation, using the original size without rounding.
+ return QRect(column, row, size.width(), size.height());
+}
+
+/*!
+ \internal
+
+ Constructs a general area allocator that is initially \a size pixels
+ in size. The \a size will be rounded up to the next power of two,
+ to simplify the internal allocation policy.
+
+ This constructor sets minimumAllocation() to (8, 8) to reduce the
+ housekeeping overhead of the internal data structures.
+*/
+QGeneralAreaAllocator::QGeneralAreaAllocator(const QSize &size)
+ : QAreaAllocator(QGL::nextPowerOfTwo(size))
+{
+ m_root = new Node();
+ m_root->rect = QRect(0, 0, m_size.width(), m_size.height());
+ m_root->largestFree = m_size;
+ m_root->parent = 0;
+ m_root->left = 0;
+ m_root->right = 0;
+ m_nodeCount = 1;
+ setMinimumAllocation(QSize(8, 8));
+}
+
+/*!
+ \internal
+
+ Destroys this general area allocator.
+*/
+QGeneralAreaAllocator::~QGeneralAreaAllocator()
+{
+ freeNode(m_root);
+}
+
+/*!
+ \internal
+*/
+void QGeneralAreaAllocator::freeNode(Node *node)
+{
+ if (node) {
+ freeNode(node->left);
+ freeNode(node->right);
+ }
+ delete node;
+}
+
+/*!
+ \internal
+
+ The \a size will be rounded up to the next power of two.
+ Use size() to determine the actual size after expansion.
+*/
+void QGeneralAreaAllocator::expand(const QSize &size)
+{
+ QAreaAllocator::expand(QGL::nextPowerOfTwo(size));
+
+ if (m_root->rect.size() == m_size)
+ return; // No change.
+ if (!m_root->left && m_root->largestFree.width() > 0) {
+ // No allocations have occurred, so just adjust the root size.
+ m_root->rect = QRect(0, 0, m_size.width(), m_size.height());
+ m_root->largestFree = m_size;
+ return;
+ }
+
+ // Add extra nodes above the current root to expand the tree.
+ Node *oldRoot = m_root;
+ Split split;
+ if (m_size.width() >= m_size.height())
+ split = SplitOnX;
+ else
+ split = SplitOnY;
+ while (m_root->rect.size() != m_size) {
+ if (m_root->rect.width() == m_size.width())
+ split = SplitOnY;
+ else if (m_root->rect.height() == m_size.height())
+ split = SplitOnX;
+ Node *parent = new Node();
+ Node *right = new Node();
+ m_nodeCount += 2;
+ m_root->parent = parent;
+ parent->parent = 0;
+ parent->left = m_root;
+ parent->right = right;
+ parent->largestFree = m_root->rect.size();
+ right->parent = parent;
+ right->left = 0;
+ right->right = 0;
+ right->largestFree = m_root->rect.size();
+ if (split == SplitOnX) {
+ parent->rect = QRect(m_root->rect.x(), m_root->rect.y(),
+ m_root->rect.width() * 2,
+ m_root->rect.height());
+ right->rect = QRect(m_root->rect.x() + m_root->rect.width(),
+ m_root->rect.y(),
+ m_root->rect.width(), m_root->rect.height());
+ } else {
+ parent->rect = QRect(m_root->rect.x(), m_root->rect.y(),
+ m_root->rect.width(),
+ m_root->rect.height() * 2);
+ right->rect = QRect(m_root->rect.x(),
+ m_root->rect.y() + m_root->rect.width(),
+ m_root->rect.width(), m_root->rect.height());
+ }
+ split = (split == SplitOnX ? SplitOnY : SplitOnX);
+ m_root = parent;
+ }
+ updateLargestFree(oldRoot);
+}
+
+static inline bool fitsWithin(const QSize &size1, const QSize &size2)
+{
+ return size1.width() <= size2.width() && size1.height() <= size2.height();
+}
+
+/*!
+ \internal
+*/
+QRect QGeneralAreaAllocator::allocate(const QSize &size)
+{
+ QSize rounded = roundAllocation(size);
+ rounded = QGL::nextPowerOfTwo(rounded);
+ if (rounded.width() <= 0 || rounded.width() > m_size.width() ||
+ rounded.height() <= 0 || rounded.height() > m_size.height())
+ return QRect();
+ QPoint point = allocateFromNode(rounded, m_root);
+ if (point.x() >= 0)
+ return QRect(point, size);
+ else
+ return QRect();
+}
+
+/*!
+ \internal
+*/
+QPoint QGeneralAreaAllocator::allocateFromNode(const QSize &size, Node *node)
+{
+ // Find the best node to insert into, which should be
+ // a node with the least amount of unused space that is
+ // big enough to contain the requested size.
+ while (node != 0) {
+ // Go down a level and determine if the left or right
+ // sub-tree contains the best chance of allocation.
+ Node *left = node->left;
+ Node *right = node->right;
+ if (left && fitsWithin(size, left->largestFree)) {
+ if (right && fitsWithin(size, right->largestFree)) {
+ if (left->largestFree.width() < right->largestFree.width() ||
+ left->largestFree.height() < right->largestFree.height()) {
+ // The largestFree values may be a little oversized,
+ // so try the left sub-tree and then the right sub-tree.
+ QPoint point = allocateFromNode(size, left);
+ if (point.x() >= 0)
+ return point;
+ else
+ return allocateFromNode(size, right);
+ } else {
+ node = right;
+ }
+ } else {
+ node = left;
+ }
+ } else if (right && fitsWithin(size, right->largestFree)) {
+ node = right;
+ } else if (left || right) {
+ // Neither sub-node has enough space to allocate from.
+ return QPoint(-1, -1);
+ } else if (fitsWithin(size, node->largestFree)) {
+ // Do we need to split this node into smaller pieces?
+ Split split;
+ if (fitsWithin(QSize(size.width() * 2, size.height() * 2),
+ node->largestFree)) {
+ // Split in either direction: choose the inverse of
+ // the parent node's split direction to try to balance
+ // out the wasted space as further subdivisions happen.
+ if (node->parent &&
+ node->parent->left->rect.x() ==
+ node->parent->right->rect.x())
+ split = SplitOnX;
+ else if (node->parent)
+ split = SplitOnY;
+ else if (node->rect.width() >= node->rect.height())
+ split = SplitOnX;
+ else
+ split = SplitOnY;
+ } else if (fitsWithin(QSize(size.width() * 2, size.height()),
+ node->largestFree)) {
+ // Split along the X direction.
+ split = SplitOnX;
+ } else if (fitsWithin(QSize(size.width(), size.height() * 2),
+ node->largestFree)) {
+ // Split along the Y direction.
+ split = SplitOnY;
+ } else {
+ // Cannot split further - allocate this node.
+ node->largestFree = QSize(0, 0);
+ updateLargestFree(node);
+ return node->rect.topLeft();
+ }
+
+ // Split the node, then go around again using the left sub-tree.
+ node = splitNode(node, split);
+ } else {
+ // Cannot possibly fit into this node.
+ break;
+ }
+ }
+ return QPoint(-1, -1);
+}
+
+/*!
+ \internal
+*/
+QGeneralAreaAllocator::Node *QGeneralAreaAllocator::splitNode
+ (Node *node, Split split)
+{
+ Node *left = new Node();
+ Node *right = new Node();
+ m_nodeCount += 2;
+ left->parent = node;
+ left->left = 0;
+ left->right = 0;
+ right->parent = node;
+ right->left = 0;
+ right->right = 0;
+ node->left = left;
+ node->right = right;
+ if (split == SplitOnX) {
+ left->rect = QRect(node->rect.x(), node->rect.y(),
+ node->rect.width() / 2,
+ node->rect.height());
+ right->rect = QRect(left->rect.right() + 1, node->rect.y(),
+ node->rect.width() / 2,
+ node->rect.height());
+ } else {
+ left->rect = QRect(node->rect.x(), node->rect.y(),
+ node->rect.width(),
+ node->rect.height() / 2);
+ right->rect = QRect(node->rect.x(), left->rect.bottom() + 1,
+ node->rect.width(),
+ node->rect.height() / 2);
+ }
+ left->largestFree = left->rect.size();
+ right->largestFree = right->rect.size();
+ node->largestFree = right->largestFree;
+ return left;
+}
+
+/*!
+ \internal
+*/
+void QGeneralAreaAllocator::updateLargestFree(Node *node)
+{
+ while ((node = node->parent) != 0) {
+ node->largestFree =
+ QSize(qMax(node->left->largestFree.width(),
+ node->right->largestFree.width()),
+ qMax(node->left->largestFree.height(),
+ node->right->largestFree.height()));
+ }
+}
+
+/*!
+ \internal
+*/
+void QGeneralAreaAllocator::release(const QRect &rect)
+{
+ // Locate the node that contains the allocated region.
+ Node *node = m_root;
+ QPoint point = rect.topLeft();
+ while (node != 0) {
+ if (node->left && node->left->rect.contains(point))
+ node = node->left;
+ else if (node->right && node->right->rect.contains(point))
+ node = node->right;
+ else if (node->rect.contains(point))
+ break;
+ else
+ return; // Point is completely outside the tree.
+ }
+ if (!node)
+ return;
+
+ // Mark the node as free and then work upwards through the tree
+ // recombining and deleting nodes until we reach a sibling
+ // that is still allocated.
+ node->largestFree = node->rect.size();
+ while (node->parent) {
+ if (node->parent->left == node) {
+ if (node->parent->right->largestFree !=
+ node->parent->right->rect.size())
+ break;
+ } else {
+ if (node->parent->left->largestFree !=
+ node->parent->left->rect.size())
+ break;
+ }
+ node = node->parent;
+ freeNode(node->left);
+ freeNode(node->right);
+ m_nodeCount -= 2;
+ node->left = 0;
+ node->right = 0;
+ node->largestFree = node->rect.size();
+ }
+
+ // Make the rest of our ancestors have the correct "largest free size".
+ updateLargestFree(node);
+}
+
+/*!
+ \internal
+*/
+int QGeneralAreaAllocator::overhead() const
+{
+ return m_nodeCount * sizeof(Node);
+}
+
+/*!
+ \internal
+
+ Constructs a uniform area allocator that is initially \a size pixels
+ in size. The \a uniformSize specifies the single allocation size
+ that is supported. All allocate() requests must be \a uniformSize
+ or less.
+*/
+QUniformAreaAllocator::QUniformAreaAllocator
+ (const QSize &size, const QSize &uniformSize)
+ : QAreaAllocator(size), m_uniformSize(uniformSize), m_firstFree(0)
+{
+ Q_ASSERT(uniformSize.width() > 0 && uniformSize.height() > 0);
+ Q_ASSERT(size.width() >= uniformSize.width() &&
+ size.height() >= uniformSize.height());
+ m_gridSize = QSize(size.width() / uniformSize.width(),
+ size.height() / uniformSize.height());
+ int count = m_gridSize.width() * m_gridSize.height();
+ m_grid = new int [count];
+ for (int index = 0; index < (count - 1); ++index)
+ m_grid[index] = index + 1;
+ m_grid[count - 1] = -1;
+}
+
+/*!
+ \internal
+
+ Destroys this uniform area allocator.
+*/
+QUniformAreaAllocator::~QUniformAreaAllocator()
+{
+ delete [] m_grid;
+}
+
+/*!
+ \fn QSize QUniformAreaAllocator::uniformSize() const
+ \internal
+
+ Returns the uniform size of all allocations.
+
+ \sa allocate()
+*/
+
+/*!
+ \internal
+*/
+void QUniformAreaAllocator::expand(const QSize &size)
+{
+ QAreaAllocator::expand(size);
+
+ QSize newGridSize = QSize(m_size.width() / m_uniformSize.width(),
+ m_size.height() / m_uniformSize.height());
+ if (m_gridSize == newGridSize)
+ return;
+
+ // Create a new grid.
+ int newCount = newGridSize.width() * newGridSize.height();
+ int *newGrid = new int [newCount];
+
+ // Copy across the free blocks from the old grid.
+ int posn = m_firstFree;
+ int newFirstFree = -1;
+ while (posn != -1) {
+ int x = posn % m_gridSize.width();
+ int y = posn / m_gridSize.width();
+ int newPosn = x + y * newGridSize.width();
+ newGrid[newPosn] = newFirstFree;
+ newFirstFree = newPosn;
+ posn = m_grid[posn];
+ }
+
+ // Add free blocks within the expanded area of the new grid.
+ for (int y = 0; y < m_gridSize.height(); ++y) {
+ int newPosn = y * newGridSize.width() + m_gridSize.width();
+ for (int x = m_gridSize.width(); x < newGridSize.width(); ++x) {
+ newGrid[newPosn] = newFirstFree;
+ newFirstFree = newPosn;
+ ++newPosn;
+ }
+ }
+ for (int y = m_gridSize.height(); y < newGridSize.height(); ++y) {
+ int newPosn = y * newGridSize.width();
+ for (int x = 0; x < newGridSize.width(); ++x) {
+ newGrid[newPosn] = newFirstFree;
+ newFirstFree = newPosn;
+ ++newPosn;
+ }
+ }
+
+ // Replace the old grid.
+ delete [] m_grid;
+ m_grid = newGrid;
+ m_gridSize = newGridSize;
+ m_firstFree = newFirstFree;
+}
+
+/*!
+ \internal
+*/
+QRect QUniformAreaAllocator::allocate(const QSize &size)
+{
+ QSize rounded = roundAllocation(size);
+ if (rounded.width() > m_uniformSize.width() ||
+ rounded.height() > m_uniformSize.height())
+ return QRect();
+ int posn = m_firstFree;
+ if (posn == -1)
+ return QRect();
+ m_firstFree = m_grid[posn];
+ int x = posn % m_gridSize.width();
+ int y = posn / m_gridSize.width();
+ return QRect(x * m_uniformSize.width(), y * m_uniformSize.height(),
+ size.width(), size.height());
+}
+
+/*!
+ \internal
+*/
+void QUniformAreaAllocator::release(const QRect &rect)
+{
+ int x = rect.x() / m_uniformSize.width();
+ int y = rect.y() / m_uniformSize.height();
+ int posn = x + y * m_gridSize.width();
+ Q_ASSERT(posn >= 0 && posn < m_gridSize.width() * m_gridSize.height());
+ m_grid[posn] = m_firstFree;
+ m_firstFree = posn;
+}
+
+/*!
+ \internal
+*/
+int QUniformAreaAllocator::overhead() const
+{
+ return sizeof(int) * m_gridSize.width() * m_gridSize.height();
+}
+
+QT_END_NAMESPACE
diff --git a/src/threed/textures/qareaallocator.h b/src/threed/textures/qareaallocator.h
new file mode 100644
index 000000000..a4d4f3dff
--- /dev/null
+++ b/src/threed/textures/qareaallocator.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QAREAALLOCATOR_P_H
+#define QAREAALLOCATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt 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.
+//
+
+#include "qt3dglobal.h"
+#include <QtCore/qsize.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class Q_QT3D_EXPORT QAreaAllocator
+{
+public:
+ QAreaAllocator(const QSize &size);
+ virtual ~QAreaAllocator();
+
+ QSize size() const { return m_size; }
+
+ QSize minimumAllocation() const { return m_minAlloc; }
+ void setMinimumAllocation(const QSize &size) { m_minAlloc = size; }
+
+ QSize margin() const { return m_margin; }
+ void setMargin(const QSize &margin) { m_margin = margin; }
+
+ virtual void expand(const QSize &size);
+ void expandBy(const QSize &size);
+
+ virtual QRect allocate(const QSize &size) = 0;
+ virtual QList<QRect> allocate(const QList<QSize> &sizes);
+ virtual void release(const QRect &rect);
+ virtual void release(const QList<QRect> &rects);
+
+ virtual int overhead() const;
+
+protected:
+ QSize m_size;
+ QSize m_minAlloc;
+ QSize m_margin;
+
+ QSize roundAllocation(const QSize &size) const;
+};
+
+class Q_QT3D_EXPORT QSimpleAreaAllocator : public QAreaAllocator
+{
+public:
+ QSimpleAreaAllocator(const QSize &size);
+ virtual ~QSimpleAreaAllocator();
+
+ QRect allocate(const QSize &size);
+
+private:
+ int m_row;
+ int m_column;
+ int m_rowHeight;
+};
+
+class Q_QT3D_EXPORT QGeneralAreaAllocator : public QAreaAllocator
+{
+public:
+ QGeneralAreaAllocator(const QSize &size);
+ virtual ~QGeneralAreaAllocator();
+
+ void expand(const QSize &size);
+ QRect allocate(const QSize &size);
+ void release(const QRect &rect);
+ int overhead() const;
+
+private:
+ enum Split { SplitOnX, SplitOnY };
+
+ struct Node
+ {
+ QRect rect;
+ QSize largestFree;
+ Node *parent;
+ Node *left;
+ Node *right;
+ };
+
+ Node *m_root;
+ int m_nodeCount;
+
+ static void freeNode(Node *node);
+ QPoint allocateFromNode(const QSize &size, Node *node);
+ Node *splitNode(Node *node, Split split);
+ static void updateLargestFree(Node *node);
+};
+
+class Q_QT3D_EXPORT QUniformAreaAllocator : public QAreaAllocator
+{
+public:
+ QUniformAreaAllocator(const QSize &size, const QSize &uniformSize);
+ virtual ~QUniformAreaAllocator();
+
+ QSize uniformSize() const { return m_uniformSize; }
+
+ void expand(const QSize &size);
+ QRect allocate(const QSize &size);
+ void release(const QRect &rect);
+ int overhead() const;
+
+private:
+ QSize m_uniformSize;
+ QSize m_gridSize;
+ int *m_grid;
+ int m_firstFree;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/threed/textures/qglsharedresource.cpp b/src/threed/textures/qglsharedresource.cpp
new file mode 100644
index 000000000..91bef8972
--- /dev/null
+++ b/src/threed/textures/qglsharedresource.cpp
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglsharedresource_p.h"
+#include <QtCore/qmutex.h>
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(Q_MOC_RUN)
+
+class Q_OPENGL_EXPORT QGLSignalProxy : public QObject
+{
+ Q_OBJECT
+public:
+ QGLSignalProxy() : QObject() {}
+ void emitAboutToDestroyContext(const QGLContext *context) {
+ emit aboutToDestroyContext(context);
+ }
+ static QGLSignalProxy *instance();
+Q_SIGNALS:
+ void aboutToDestroyContext(const QGLContext *context);
+};
+
+#endif
+
+class QGLContextInfo
+{
+public:
+ QGLContextInfo(const QGLContext *ctx) : m_context(ctx), m_resources(0) {}
+ ~QGLContextInfo();
+
+ const QGLContext *m_context;
+ QGLSharedResource *m_resources;
+};
+
+QGLContextInfo::~QGLContextInfo()
+{
+ // Detach this information block from all of the shared resources
+ // that used to be owned by it.
+ QGLSharedResource *resource = m_resources;
+ while (resource != 0) {
+ resource->m_contextInfo = 0;
+ resource->m_id = 0;
+ resource = resource->m_next;
+ }
+}
+
+class QGLContextManager : public QObject
+{
+ Q_OBJECT
+public:
+ QGLContextManager(QObject *parent = 0);
+ ~QGLContextManager();
+
+ QMutex managerLock;
+
+ QGLContextInfo *contextInfo(const QGLContext *ctx);
+
+private Q_SLOTS:
+ void aboutToDestroyContext(const QGLContext *ctx);
+
+private:
+ QList<QGLContextInfo *> m_contexts;
+};
+
+Q_GLOBAL_STATIC(QGLContextManager, qt_gl_context_manager)
+
+QGLContextManager::QGLContextManager(QObject *parent)
+ : QObject(parent)
+{
+ QGLSignalProxy *proxy = QGLSignalProxy::instance();
+ QThread *mainThread = qApp->thread();
+ if (thread() != mainThread) {
+ // The manager and signal proxy have been created for the first
+ // time in a background thread. For safety, move both objects
+ // to the main thread.
+ moveToThread(mainThread);
+ proxy->moveToThread(mainThread);
+ }
+ connect(proxy, SIGNAL(aboutToDestroyContext(const QGLContext *)),
+ this, SLOT(aboutToDestroyContext(const QGLContext *)));
+}
+
+QGLContextManager::~QGLContextManager()
+{
+ QMutexLocker locker(&managerLock);
+ qDeleteAll(m_contexts);
+}
+
+QGLContextInfo *QGLContextManager::contextInfo(const QGLContext *ctx)
+{
+ QGLContextInfo *info;
+ for (int index = 0; index < m_contexts.size(); ++index) {
+ info = m_contexts[index];
+ if (info->m_context == ctx)
+ return info;
+ }
+ info = new QGLContextInfo(ctx);
+ m_contexts.append(info);
+ return info;
+}
+
+Q_OPENGL_EXPORT const QGLContext *qt_gl_transfer_context(const QGLContext *);
+
+void QGLContextManager::aboutToDestroyContext(const QGLContext *ctx)
+{
+ QMutexLocker locker(&managerLock);
+ int index = 0;
+ while (index < m_contexts.size()) {
+ QGLContextInfo *info = m_contexts[index];
+ if (info->m_context == ctx) {
+ const QGLContext *transfer = qt_gl_transfer_context(ctx);
+ if (transfer) {
+ // Transfer ownership to another context in the same sharing
+ // group. This may result in multiple QGLContextInfo objects
+ // for the same context, which is ok.
+ info->m_context = transfer;
+ } else {
+ // All contexts in the sharing group have been deleted,
+ // so detach all of the shared resources.
+ m_contexts.removeAt(index);
+ delete info;
+ continue;
+ }
+ }
+ ++index;
+ }
+}
+
+const QGLContext *QGLSharedResource::context() const
+{
+ // Hope that the context will not be destroyed in another thread
+ // while we are doing this so we don't have to acquire the lock.
+ return m_contextInfo ? m_contextInfo->m_context : 0;
+}
+
+void QGLSharedResource::attach(const QGLContext *context, GLuint id)
+{
+ Q_ASSERT(!m_contextInfo);
+ QGLContextManager *manager = qt_gl_context_manager();
+ QMutexLocker locker(&(manager->managerLock));
+ m_contextInfo = manager->contextInfo(context);
+ m_id = id;
+ m_next = m_contextInfo->m_resources;
+ m_prev = 0;
+ if (m_contextInfo->m_resources)
+ m_contextInfo->m_resources->m_prev = this;
+ m_contextInfo->m_resources = this;
+}
+
+void QGLSharedResource::destroy()
+{
+ // Detach this resource from the context information block.
+ QGLContextManager *manager = qt_gl_context_manager();
+ const QGLContext *owner = 0;
+ GLuint id = 0;
+ manager->managerLock.lock();
+ if (m_contextInfo) {
+ if (m_next)
+ m_next->m_prev = m_prev;
+ if (m_prev)
+ m_prev->m_next = m_next;
+ else
+ m_contextInfo->m_resources = m_next;
+ owner = m_contextInfo->m_context;
+ id = m_id;
+ }
+ m_contextInfo = 0;
+ m_id = 0;
+ m_next = 0;
+ m_prev = 0;
+ manager->managerLock.unlock();
+
+ // Switch back to the owning context temporarily and delete the id.
+ if (owner && id) {
+ QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ QGLContext *oldContext;
+ QGLContext *doneContext;
+ if (currentContext != owner && !QGLContext::areSharing(owner, currentContext)) {
+ oldContext = currentContext;
+ doneContext = const_cast<QGLContext *>(owner);
+ doneContext->makeCurrent();
+ } else {
+ oldContext = 0;
+ doneContext = 0;
+ }
+ m_destroyFunc(id);
+ if (oldContext)
+ oldContext->makeCurrent();
+ else if (!currentContext && doneContext)
+ doneContext->doneCurrent();
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qglsharedresource.moc"
diff --git a/src/threed/textures/qglsharedresource_p.h b/src/threed/textures/qglsharedresource_p.h
new file mode 100644
index 000000000..9aafe70a8
--- /dev/null
+++ b/src/threed/textures/qglsharedresource_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLSHAREDRESOURCE_P_H
+#define QGLSHAREDRESOURCE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt 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.
+//
+
+#include <QtOpenGL/qgl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QGLContextManager;
+class QGLContextInfo;
+
+class QGLSharedResource
+{
+public:
+ typedef void (*DestroyResourceFunc)(GLuint id);
+ QGLSharedResource(DestroyResourceFunc destroyFunc)
+ : m_destroyFunc(destroyFunc), m_contextInfo(0), m_id(0)
+ , m_next(0), m_prev(0) {}
+ ~QGLSharedResource() { destroy(); }
+
+ const QGLContext *context() const;
+ GLuint id() const { return m_id; }
+ void clearId() { m_id = 0; }
+
+ void attach(const QGLContext *context, GLuint id);
+ void destroy();
+
+private:
+ DestroyResourceFunc m_destroyFunc;
+ QGLContextInfo *m_contextInfo;
+ GLuint m_id;
+ QGLSharedResource *m_next;
+ QGLSharedResource *m_prev;
+
+ friend class QGLContextManager;
+ friend class QGLContextInfo;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/threed/textures/qgltexture2d.cpp b/src/threed/textures/qgltexture2d.cpp
new file mode 100644
index 000000000..d45a1dc04
--- /dev/null
+++ b/src/threed/textures/qgltexture2d.cpp
@@ -0,0 +1,697 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgltexture2d.h"
+#include "qgltexture2d_p.h"
+#include "qgltextureutils_p.h"
+#include "qglpainter_p.h"
+#include "qglext_p.h"
+
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLTexture2D
+ \brief The QGLTexture2D class represents a 2D texture object for GL painting operations.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::textures
+
+ QGLTexture2D contains a QImage and settings for texture filters,
+ wrap modes, and mipmap generation. When bind() is called, this
+ information is uploaded to the GL server if it has changed since
+ the last time bind() was called.
+
+ Once a QGLTexture2D object is created, it can be bound to multiple
+ GL contexts. Internally, a separate texture identifier is created
+ for each context. This makes QGLTexture2D easier to use than
+ raw GL texture identifiers because the application does not need
+ to be as concerned with whether the texture identifier is valid
+ in the current context. The application merely calls bind() and
+ QGLTexture2D will create a new texture identifier for the context
+ if necessary.
+
+ QGLTexture2D internally points to a reference-counted object that
+ represents the current texture state. If the QGLTexture2D is copied,
+ the internal pointer is the same. Modifications to one QGLTexture2D
+ copy will affect all of the other copies in the system.
+
+ The texture identifiers will be destroyed when the last QGLTexture2D
+ reference is destroyed, or when a context is destroyed that contained a
+ texture identifier that was created by QGLTexture2D.
+
+ QGLTexture2D can also be used for uploading 1D textures into the
+ GL server by specifying an image() with a height of 1.
+
+ \sa QGLTextureCube
+*/
+
+QGLTexture2DPrivate::QGLTexture2DPrivate()
+{
+ horizontalWrap = QGL::Repeat;
+ verticalWrap = QGL::Repeat;
+ bindOptions = QGLContext::DefaultBindOption;
+#if !defined(QT_OPENGL_ES)
+ mipmapSupported = false;
+ mipmapSupportedKnown = false;
+#endif
+ imageGeneration = 0;
+ parameterGeneration = 0;
+ infos = 0;
+}
+
+QGLTexture2DPrivate::~QGLTexture2DPrivate()
+{
+ // Destroy the texture id's in the GL server in their original contexts.
+ QGLTexture2DTextureInfo *current = infos;
+ QGLTexture2DTextureInfo *next;
+ const QGLContext *currentContext =
+ const_cast<QGLContext *>(QGLContext::currentContext());
+ const QGLContext *firstContext = currentContext;
+ while (current != 0) {
+ next = current->next;
+ if (current->isLiteral)
+ current->tex.clearId(); // Don't delete literal id's.
+ delete current;
+ current = next;
+ }
+ if (firstContext != currentContext) {
+ if (firstContext)
+ const_cast<QGLContext *>(firstContext)->makeCurrent();
+ else if (currentContext)
+ const_cast<QGLContext *>(currentContext)->doneCurrent();
+ }
+}
+
+/*!
+ Constructs a null texture object and attaches it to \a parent.
+
+ \sa isNull()
+*/
+QGLTexture2D::QGLTexture2D(QObject *parent)
+ : QObject(parent), d_ptr(new QGLTexture2DPrivate())
+{
+}
+
+/*!
+ Destroys this texture object. If this object is the last
+ reference to the underlying GL texture, then the underlying
+ GL texture will also be deleted.
+*/
+QGLTexture2D::~QGLTexture2D()
+{
+}
+
+/*!
+ Returns true if this texture object is null; that is, image()
+ is null and textureId() is zero.
+*/
+bool QGLTexture2D::isNull() const
+{
+ Q_D(const QGLTexture2D);
+ return d->image.isNull() && !d->infos;
+}
+
+/*!
+ Returns true if this texture has an alpha channel; false if the
+ texture is fully opaque.
+*/
+bool QGLTexture2D::hasAlphaChannel() const
+{
+ Q_D(const QGLTexture2D);
+ if (!d->image.isNull())
+ return d->image.hasAlphaChannel();
+ QGLTexture2DTextureInfo *info = d->infos;
+ if (info)
+ return info->tex.hasAlpha();
+ return false;
+}
+
+/*!
+ Returns the size of this texture. If the underlying OpenGL
+ implementation requires texture sizes to be a power of two,
+ then this function will return the next power of two equal
+ to or greater than requestedSize()
+
+ \sa setSize(), requestedSize()
+*/
+QSize QGLTexture2D::size() const
+{
+ Q_D(const QGLTexture2D);
+ return d->size;
+}
+
+/*!
+ Sets the size of this texture to \a value. If the underlying
+ OpenGL implementation requires texture sizes to be a power of
+ two, then requestedSize() will be set to \a value, and the
+ actual size will be set to the next power of two equal
+ to or greater than \a value. Otherwise both size() and
+ requestedSize() will be set to \a value.
+
+ \sa size(), requestedSize()
+*/
+void QGLTexture2D::setSize(const QSize& value)
+{
+ Q_D(QGLTexture2D);
+ if (d->requestedSize == value)
+ return;
+ if (!(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0)
+ && !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))
+ d->size = QGL::nextPowerOfTwo(value);
+ else
+ d->size = value;
+ d->requestedSize = value;
+ ++(d->imageGeneration);
+}
+
+/*!
+ Returns the size that was previously set with setSize() before
+ it was rounded to a power of two.
+
+ \sa size(), setSize()
+*/
+QSize QGLTexture2D::requestedSize() const
+{
+ Q_D(const QGLTexture2D);
+ return d->requestedSize;
+}
+
+/*!
+ Returns the image that is currently associated with this texture.
+ The image may not have been uploaded into the GL server yet.
+ Uploads occur upon the next call to bind().
+
+ \sa setImage()
+*/
+QImage QGLTexture2D::image() const
+{
+ Q_D(const QGLTexture2D);
+ return d->image;
+}
+
+/*!
+ Sets the \a image that is associated with this texture. The image
+ will be uploaded into the GL server the next time bind() is called.
+
+ If setSize() or setImage() has been called previously, then \a image
+ will be scaled to size() when it is uploaded.
+
+ If \a image is null, then this function is equivalent to clearImage().
+
+ \sa image(), setSize(), copyImage(), setPixmap()
+*/
+void QGLTexture2D::setImage(const QImage& image)
+{
+ Q_D(QGLTexture2D);
+ d->compressedData = QByteArray(); // Clear compressed file data.
+ if (image.isNull()) {
+ // Don't change the imageGeneration, because we aren't actually
+ // changing the image in the GL server, only the client copy.
+ d->image = image;
+ } else {
+ if (!d->size.isValid())
+ setSize(image.size());
+ d->image = image;
+ ++(d->imageGeneration);
+ }
+}
+
+/*!
+ Sets the image that is associated with this texture to \a pixmap.
+
+ This is a convenience that calls setImage() after converting
+ \a pixmap into a QImage. It may be more efficient on some
+ platforms than the application calling QPixmap::toImage().
+
+ \sa setImage()
+*/
+void QGLTexture2D::setPixmap(const QPixmap& pixmap)
+{
+ QImage image = pixmap.toImage();
+ if (pixmap.depth() == 16 && !image.hasAlphaChannel()) {
+ // If the system depth is 16 and the pixmap doesn't have an alpha channel
+ // then we convert it to RGB16 in the hope that it gets uploaded as a 16
+ // bit texture which is much faster to access than a 32-bit one.
+ image = image.convertToFormat(QImage::Format_RGB16);
+ }
+ setImage(image);
+}
+
+/*!
+ Clears the image() that is associated with this texture, but the
+ GL texture will retain its current value. This can be used to
+ release client-side memory that is no longer required once the
+ image has been uploaded into the GL server.
+
+ The following code will queue \c image to be uploaded, immediately
+ force it to be uploaded into the current GL context, and then
+ clear the client copy:
+
+ \code
+ texture.setImage(image);
+ texture.bind();
+ texture.clearImage()
+ \endcode
+
+ \sa image(), setImage()
+*/
+void QGLTexture2D::clearImage()
+{
+ Q_D(QGLTexture2D);
+ d->image = QImage();
+}
+
+#ifndef GL_GENERATE_MIPMAP_SGIS
+#define GL_GENERATE_MIPMAP_SGIS 0x8191
+#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192
+#endif
+
+/*!
+ Sets this texture to the contents of a compressed image file
+ at \a path. Returns true if the file exists and has a supported
+ compressed format; false otherwise.
+
+ The DDS, ETC1, PVRTC2, and PVRTC4 compression formats are
+ supported, assuming that the GL implementation has the
+ appropriate extension.
+
+ \sa setImage(), setSize()
+*/
+bool QGLTexture2D::setCompressedFile(const QString &path)
+{
+ Q_D(QGLTexture2D);
+ d->image = QImage();
+ QFile f(path);
+ if (!f.open(QIODevice::ReadOnly))
+ {
+ qWarning("QGLTexture2D::setCompressedFile(%s): File could not be read",
+ qPrintable(path));
+ return false;
+ }
+ QByteArray data = f.readAll();
+ f.close();
+
+ bool hasAlpha, isFlipped;
+ if (!QGLBoundTexture::canBindCompressedTexture
+ (data.constData(), data.size(), 0, &hasAlpha, &isFlipped)) {
+ qWarning("QGLTexture2D::setCompressedFile(%s): Format is not supported",
+ path.toLocal8Bit().constData());
+ return false;
+ }
+
+ QFileInfo fi(path);
+ d->url = QUrl::fromLocalFile(fi.absoluteFilePath());
+
+ // The 3DS loader expects the flip state to be set before bind().
+ if (isFlipped)
+ d->bindOptions &= ~QGLContext::InvertedYBindOption;
+ else
+ d->bindOptions |= QGLContext::InvertedYBindOption;
+
+ d->compressedData = data;
+ ++(d->imageGeneration);
+ return true;
+}
+
+/*!
+ Returns the url that was last set with setUrl.
+*/
+QUrl QGLTexture2D::url() const
+{
+ Q_D(const QGLTexture2D);
+ return d->url;
+}
+
+/*!
+ Sets this texture to have the contents of the image stored at \a url.
+*/
+void QGLTexture2D::setUrl(const QUrl &url)
+{
+ Q_D(QGLTexture2D);
+ if (d->url == url)
+ return;
+ d->url = url;
+
+ if (url.isEmpty())
+ {
+ d->image = QImage();
+ }
+ else
+ {
+ if (url.scheme() == QLatin1String("file"))
+ {
+ QString fileName = url.toLocalFile();
+ if (fileName.endsWith(QLatin1String(".dds"), Qt::CaseInsensitive))
+ {
+ setCompressedFile(fileName);
+ }
+ else
+ {
+ QImage im(fileName);
+ if (im.isNull())
+ qWarning("Could not load texture: %s", qPrintable(fileName));
+ setImage(im);
+ }
+ }
+ else
+ {
+ qWarning("Network URLs not yet supported");
+ /*
+ if (d->textureReply)
+ d->textureReply->deleteLater();
+ QNetworkRequest req(d->textureUrl);
+ req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
+ d->textureReply = qmlEngine(this)->networkAccessManager()->get(req);
+ QObject::connect(d->textureReply, SIGNAL(finished()),
+ this, SLOT(textureRequestFinished()));
+ */
+ }
+ }
+}
+
+/*!
+ Copies the contents of \a image to \a offset in this texture
+ within the current GL context.
+
+ Unlike setImage(), this function copies the image data to the
+ GL server immediately using \c{glTexSubImage2D()}. This is typically
+ used to update the contents of a texture after it has been created.
+
+ It is assumed that the application has already called bind() on
+ this texture to bind it to the current GL context.
+
+ If the texture has been created in multiple contexts, only the
+ texture identifier for the current context will be updated.
+
+ \sa setImage(), bind()
+*/
+void QGLTexture2D::copyImage(const QImage& image, const QPoint& offset)
+{
+ QImage img = QGLWidget::convertToGLFormat(image);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, offset.x(), offset.y(),
+ img.width(), img.height(), GL_RGBA,
+ GL_UNSIGNED_BYTE, img.bits());
+#if defined(QT_OPENGL_ES_2)
+ Q_D(QGLTexture2D);
+ if (d->bindOptions & QGLContext::MipmapBindOption)
+ glGenerateMipmap(GL_TEXTURE_2D);
+#endif
+}
+
+/*!
+ Returns the options to use when binding the image() to an OpenGL
+ context for the first time. The default options are
+ QGLContext::LinearFilteringBindOption |
+ QGLContext::InvertedYBindOption | QGLContext::MipmapBindOption.
+
+ \sa setBindOptions()
+*/
+QGLContext::BindOptions QGLTexture2D::bindOptions() const
+{
+ Q_D(const QGLTexture2D);
+ return d->bindOptions;
+}
+
+/*!
+ Sets the \a options to use when binding the image() to an
+ OpenGL context. If the image() has already been bound,
+ then changing the options will cause it to be recreated
+ from image() the next time bind() is called.
+
+ \sa bindOptions(), bind()
+*/
+void QGLTexture2D::setBindOptions(QGLContext::BindOptions options)
+{
+ Q_D(QGLTexture2D);
+ if (d->bindOptions != options) {
+ d->bindOptions = options;
+ ++(d->imageGeneration);
+ }
+}
+
+/*!
+ Returns the wrapping mode for horizontal texture co-ordinates.
+ The default value is QGL::Repeat.
+
+ \sa setHorizontalWrap(), verticalWrap()
+*/
+QGL::TextureWrap QGLTexture2D::horizontalWrap() const
+{
+ Q_D(const QGLTexture2D);
+ return d->horizontalWrap;
+}
+
+/*!
+ Sets the wrapping mode for horizontal texture co-ordinates to \a value.
+
+ If \a value is not supported by the OpenGL implementation, it will be
+ replaced with a value that is supported. If the application desires a
+ very specific \a value, it can call horizontalWrap() to check that
+ the specific value was actually set.
+
+ The \a value will not be applied to the texture in the GL
+ server until the next call to bind().
+
+ \sa horizontalWrap(), setVerticalWrap()
+*/
+void QGLTexture2D::setHorizontalWrap(QGL::TextureWrap value)
+{
+ Q_D(QGLTexture2D);
+ value = qt_gl_modify_texture_wrap(value);
+ if (d->horizontalWrap != value) {
+ d->horizontalWrap = value;
+ ++(d->parameterGeneration);
+ }
+}
+
+/*!
+ Returns the wrapping mode for vertical texture co-ordinates.
+ The default value is QGL::Repeat.
+
+ \sa setVerticalWrap(), horizontalWrap()
+*/
+QGL::TextureWrap QGLTexture2D::verticalWrap() const
+{
+ Q_D(const QGLTexture2D);
+ return d->verticalWrap;
+}
+
+/*!
+ Sets the wrapping mode for vertical texture co-ordinates to \a value.
+
+ If \a value is not supported by the OpenGL implementation, it will be
+ replaced with a value that is supported. If the application desires a
+ very specific \a value, it can call verticalWrap() to check that
+ the specific value was actually set.
+
+ The \a value will not be applied to the texture in the GL
+ server until the next call to bind().
+
+ \sa verticalWrap(), setHorizontalWrap()
+*/
+void QGLTexture2D::setVerticalWrap(QGL::TextureWrap value)
+{
+ Q_D(QGLTexture2D);
+ value = qt_gl_modify_texture_wrap(value);
+ if (d->verticalWrap != value) {
+ d->verticalWrap = value;
+ ++(d->parameterGeneration);
+ }
+}
+
+/*!
+ Binds this texture to the 2D texture target.
+
+ If this texture object is not associated with an identifier in
+ the current context, then a new identifier will be created,
+ and image() uploaded into the GL server.
+
+ If setImage() or setSize() was called since the last upload,
+ then image() will be re-uploaded to the GL server.
+
+ Returns false if the texture could not be bound for some reason.
+
+ \sa release(), textureId(), setImage()
+*/
+bool QGLTexture2D::bind() const
+{
+ Q_D(const QGLTexture2D);
+ return const_cast<QGLTexture2DPrivate *>(d)->bind(GL_TEXTURE_2D);
+}
+
+bool QGLTexture2DPrivate::bind(GLenum target)
+{
+ // Get the current context. If we don't have one, then we
+ // cannot bind the texture.
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (!ctx)
+ return false;
+
+ // Find the information block for the context, or create one.
+ QGLTexture2DTextureInfo *info = infos;
+ QGLTexture2DTextureInfo *prev = 0;
+ while (info != 0 && !QGLContext::areSharing(info->tex.context(), ctx)) {
+ if (info->isLiteral)
+ return false; // Cannot create extra texture id's for literals.
+ prev = info;
+ info = info->next;
+ }
+ if (!info) {
+ info = new QGLTexture2DTextureInfo
+ (ctx, 0, imageGeneration - 1, parameterGeneration - 1);
+ if (prev)
+ prev->next = info;
+ else
+ infos = info;
+ }
+
+ if (!info->tex.textureId() || imageGeneration != info->imageGeneration) {
+ // Create the texture contents and upload a new image.
+ info->tex.setOptions(bindOptions);
+ if (!compressedData.isEmpty()) {
+ info->tex.bindCompressedTexture
+ (compressedData.constData(), compressedData.size());
+ } else {
+ info->tex.startUpload(ctx, target, image.size());
+ bindImages(info);
+ info->tex.finishUpload(target);
+ }
+ info->imageGeneration = imageGeneration;
+ } else {
+ // Bind the existing texture to the texture target.
+ glBindTexture(target, info->tex.textureId());
+ }
+
+ // If the parameter generation has changed, then alter the parameters.
+ if (parameterGeneration != info->parameterGeneration) {
+ info->parameterGeneration = parameterGeneration;
+ q_glTexParameteri(target, GL_TEXTURE_WRAP_S, horizontalWrap);
+ q_glTexParameteri(target, GL_TEXTURE_WRAP_T, verticalWrap);
+ }
+
+ // Texture is ready to be used.
+ return true;
+}
+
+void QGLTexture2DPrivate::bindImages(QGLTexture2DTextureInfo *info)
+{
+ QSize scaledSize(size);
+#if defined(QT_OPENGL_ES_2)
+ if ((bindOptions & QGLContext::MipmapBindOption) ||
+ horizontalWrap != QGL::ClampToEdge ||
+ verticalWrap != QGL::ClampToEdge) {
+ // ES 2.0 does not support NPOT textures when mipmaps are in use,
+ // or if the wrap mode isn't ClampToEdge.
+ scaledSize = QGL::nextPowerOfTwo(scaledSize);
+ }
+#endif
+ if (!image.isNull())
+ info->tex.uploadFace(GL_TEXTURE_2D, image, scaledSize);
+ else if (size.isValid())
+ info->tex.createFace(GL_TEXTURE_2D, scaledSize);
+}
+
+/*!
+ Releases the texture associated with the 2D texture target.
+ This is equivalent to \c{glBindTexture(GL_TEXTURE_2D, 0)}.
+
+ \sa bind()
+*/
+void QGLTexture2D::release() const
+{
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+/*!
+ Returns the identifier associated with this texture object in
+ the current context.
+
+ Returns zero if the texture has not previously been bound to
+ the 2D texture target in the current context with bind().
+
+ \sa bind()
+*/
+GLuint QGLTexture2D::textureId() const
+{
+ Q_D(const QGLTexture2D);
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (!ctx)
+ return 0;
+ QGLTexture2DTextureInfo *info = d->infos;
+ while (info != 0 && !QGLContext::areSharing(info->tex.context(), ctx))
+ info = info->next;
+ return info ? info->tex.textureId() : 0;
+}
+
+/*!
+ Constructs a QGLTexture2D object that wraps the supplied literal
+ texture identifier \a id, with the dimensions specified by \a size.
+
+ The \a id is assumed to have been created by the application in
+ the current GL context, and it will be destroyed by the application
+ after the returned QGLTexture2D object is destroyed.
+
+ This function is intended for interfacing to existing code that
+ uses raw GL texture identifiers. The returned QGLTexture2D can
+ only be used with the current GL context.
+
+ \sa textureId()
+*/
+QGLTexture2D *QGLTexture2D::fromTextureId(GLuint id, const QSize& size)
+{
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (!id || !ctx)
+ return 0;
+
+ QGLTexture2D *texture = new QGLTexture2D();
+ if (!size.isNull())
+ texture->setSize(size);
+ QGLTexture2DTextureInfo *info = new QGLTexture2DTextureInfo
+ (ctx, id, texture->d_ptr->imageGeneration,
+ texture->d_ptr->parameterGeneration, true);
+ texture->d_ptr->infos = info;
+ return texture;
+}
+
+QT_END_NAMESPACE
diff --git a/src/threed/textures/qgltexture2d.h b/src/threed/textures/qgltexture2d.h
new file mode 100644
index 000000000..5379c6dfa
--- /dev/null
+++ b/src/threed/textures/qgltexture2d.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLTEXTURE2D_H
+#define QGLTEXTURE2D_H
+
+#include "qglnamespace.h"
+#include <QtOpenGL/qgl.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class QGLTexture2DPrivate;
+
+class Q_QT3D_EXPORT QGLTexture2D : public QObject
+{
+ Q_OBJECT
+public:
+ QGLTexture2D(QObject *parent = 0);
+ ~QGLTexture2D();
+
+ bool isNull() const;
+ bool hasAlphaChannel() const;
+
+ QSize size() const;
+ void setSize(const QSize& value);
+ QSize requestedSize() const;
+
+ QImage image() const;
+ void setImage(const QImage& image);
+ bool setCompressedFile(const QString &path);
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ void setPixmap(const QPixmap& pixmap);
+
+ void clearImage();
+
+ void copyImage(const QImage& image, const QPoint& offset = QPoint(0, 0));
+
+ QGLContext::BindOptions bindOptions() const;
+ void setBindOptions(QGLContext::BindOptions options);
+
+ QGL::TextureWrap horizontalWrap() const;
+ void setHorizontalWrap(QGL::TextureWrap value);
+
+ QGL::TextureWrap verticalWrap() const;
+ void setVerticalWrap(QGL::TextureWrap value);
+
+ bool bind() const;
+ void release() const;
+
+ GLuint textureId() const;
+
+ static QGLTexture2D *fromTextureId(GLuint id, const QSize& size);
+
+private:
+ QScopedPointer<QGLTexture2DPrivate> d_ptr;
+
+ Q_DISABLE_COPY(QGLTexture2D)
+ Q_DECLARE_PRIVATE(QGLTexture2D)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/threed/textures/qgltexture2d_p.h b/src/threed/textures/qgltexture2d_p.h
new file mode 100644
index 000000000..d226751bd
--- /dev/null
+++ b/src/threed/textures/qgltexture2d_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLTEXTURE2D_P_H
+#define QGLTEXTURE2D_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt 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.
+//
+
+#include "qgltexture2d.h"
+#include "qgltextureutils_p.h"
+#include "qurl.h"
+
+#include <QtCore/qatomic.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGLTexture2DTextureInfo
+{
+public:
+ QGLTexture2DTextureInfo
+ (const QGLContext *context, GLuint textureId, uint imageGeneration,
+ uint parameterGeneration, bool isLiteral = false)
+ {
+ if (textureId)
+ tex.setTextureId(context, textureId);
+ this->imageGeneration = imageGeneration;
+ this->parameterGeneration = parameterGeneration;
+ this->isLiteral = isLiteral;
+ this->next = 0;
+ }
+
+ QGLBoundTexture tex;
+ uint imageGeneration;
+ uint parameterGeneration;
+ bool isLiteral;
+ QGLTexture2DTextureInfo *next;
+};
+
+class DDSFormat;
+
+class QGLTexture2DPrivate
+{
+public:
+ QGLTexture2DPrivate();
+ ~QGLTexture2DPrivate();
+
+ QSize size;
+ QSize requestedSize;
+ QImage image;
+ QUrl url;
+ QByteArray compressedData;
+ QGLContext::BindOptions bindOptions;
+ QGL::TextureWrap horizontalWrap;
+ QGL::TextureWrap verticalWrap;
+#if !defined(QT_OPENGL_ES)
+ bool mipmapSupported;
+ bool mipmapSupportedKnown;
+#endif
+ uint imageGeneration;
+ uint parameterGeneration;
+ QGLTexture2DTextureInfo *infos;
+
+ bool bind(GLenum target);
+ virtual void bindImages(QGLTexture2DTextureInfo *info);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/threed/textures/qgltexturecube.cpp b/src/threed/textures/qgltexturecube.cpp
new file mode 100644
index 000000000..9947d1b65
--- /dev/null
+++ b/src/threed/textures/qgltexturecube.cpp
@@ -0,0 +1,549 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgltexturecube.h"
+#include "qgltexture2d_p.h"
+#include "qgltextureutils_p.h"
+#include "qglpainter_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLTextureCube
+ \brief The QGLTextureCube class represents a cube map texture object for GL painting operations.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::textures
+
+ QGLTextureCube contains six QImage objects for each of the cube
+ map faces and settings for texture filters, wrap modes, and mipmap
+ generation. When bind() is called, this information is uploaded to
+ the GL server if it has changed since the last time bind() was called.
+
+ Once a QGLTextureCube object is created, it can be bound to multiple
+ GL contexts. Internally, a separate texture identifier is created
+ for each context. This makes QGLTextureCube easier to use than
+ raw GL texture identifiers because the application does not need
+ to be as concerned with whether the texture identifier is valid
+ in the current context. The application merely calls bind() and
+ QGLTextureCube will create a new texture identifier for the context
+ if necessary.
+
+ QGLTextureCube internally points to a reference-counted object that
+ represents the current texture state. If the QGLTextureCube is copied,
+ the internal pointer is the same. Modifications to one QGLTextureCube
+ copy will affect all of the other copies in the system.
+
+ The texture identifiers will be destroyed when the last QGLTextureCube
+ reference is destroyed, or when a context is destroyed that contained a
+ texture identifier that was created by QGLTextureCube.
+
+ \sa QGLTexture2D
+*/
+
+/*!
+ \enum QGLTextureCube::Face
+ This enum defines the face of a cube map texture that is affected
+ by a texture operation on QGLTextureCube instances.
+
+ \value PositiveX The positive X face of the cube map.
+ \value NegativeX The negative X face of the cube map.
+ \value PositiveY The positive Y face of the cube map.
+ \value NegativeY The negative Y face of the cube map.
+ \value PositiveZ The positive Z face of the cube map.
+ \value NegativeZ The negative Z face of the cube map.
+*/
+
+class QGLTextureCubePrivate : public QGLTexture2DPrivate
+{
+public:
+ QGLTextureCubePrivate();
+ ~QGLTextureCubePrivate();
+
+ void bindImages(QGLTexture2DTextureInfo *info);
+
+ QImage otherImages[5];
+ uint changedFaces;
+};
+
+QGLTextureCubePrivate::QGLTextureCubePrivate()
+{
+ changedFaces = 0;
+}
+
+QGLTextureCubePrivate::~QGLTextureCubePrivate()
+{
+}
+
+void QGLTextureCubePrivate::bindImages(QGLTexture2DTextureInfo *info)
+{
+ QSize scaledSize(size);
+#if defined(QT_OPENGL_ES_2)
+ if ((bindOptions & QGLContext::MipmapBindOption) ||
+ horizontalWrap != QGL::ClampToEdge ||
+ verticalWrap != QGL::ClampToEdge) {
+ // ES 2.0 does not support NPOT textures when mipmaps are in use,
+ // or if the wrap mode isn't ClampToEdge.
+ scaledSize = QGL::nextPowerOfTwo(scaledSize);
+ }
+#endif
+
+ // Handle the first face.
+ if (!image.isNull())
+ info->tex.uploadFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X, image, scaledSize);
+ else if (size.isValid())
+ info->tex.createFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X, scaledSize);
+
+ // Handle the other faces.
+ for (int face = 1; face < 6; ++face) {
+ if (!otherImages[face - 1].isNull()) {
+ info->tex.uploadFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
+ otherImages[face - 1], scaledSize);
+ } else {
+ info->tex.createFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, scaledSize);
+ }
+ }
+}
+
+/*!
+ Constructs a null texture object.
+
+ \sa isNull()
+*/
+QGLTextureCube::QGLTextureCube()
+ : d_ptr(new QGLTextureCubePrivate())
+{
+}
+
+/*!
+ Destroys this texture object. If this object is the last
+ reference to the underlying GL texture, then the underlying
+ GL texture will also be deleted.
+*/
+QGLTextureCube::~QGLTextureCube()
+{
+}
+
+/*!
+ Returns true if this texture object is null; that is, all image()
+ values are null and textureId() is zero.
+*/
+bool QGLTextureCube::isNull() const
+{
+ // TODO
+ Q_D(const QGLTextureCube);
+ return !d->infos;
+}
+
+/*!
+ Returns true if this texture has an alpha channel; false if the
+ texture is fully opaque.
+*/
+bool QGLTextureCube::hasAlphaChannel() const
+{
+ Q_D(const QGLTextureCube);
+ if (!d->image.isNull() && d->image.hasAlphaChannel())
+ return true;
+ for (int face = 0; face < 5; ++face) {
+ if (!d->otherImages[face].isNull()) {
+ if (d->otherImages[face].hasAlphaChannel())
+ return true;
+ }
+ }
+ QGLTexture2DTextureInfo *info = d->infos;
+ if (info)
+ return info->tex.hasAlpha();
+ return false;
+}
+
+/*!
+ Returns the size of this texture. If the underlying OpenGL
+ implementation requires texture sizes to be a power of two,
+ then this function will return the next power of two equal
+ to or greater than requestedSize()
+
+ \sa setSize(), requestedSize()
+*/
+QSize QGLTextureCube::size() const
+{
+ Q_D(const QGLTextureCube);
+ return d->size;
+}
+
+/*!
+ Sets the size of this texture to \a value. If the underlying
+ OpenGL implementation requires texture sizes to be a power of
+ two, then requestedSize() will be set to \a value, and the
+ actual size will be set to the next power of two equal
+ to or greater than \a value. Otherwise both size() and
+ requestedSize() will be set to \a value.
+
+ \sa size(), requestedSize()
+*/
+void QGLTextureCube::setSize(const QSize& value)
+{
+ Q_D(QGLTextureCube);
+ if (d->requestedSize == value)
+ return;
+ if (!(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) &&
+ !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))
+ d->size = QGL::nextPowerOfTwo(value);
+ else
+ d->size = value;
+ d->requestedSize = value;
+ ++(d->imageGeneration);
+}
+
+/*!
+ Returns the size that was previously set with setSize() before
+ it was rounded to a power of two.
+
+ \sa size(), setSize()
+*/
+QSize QGLTextureCube::requestedSize() const
+{
+ Q_D(const QGLTextureCube);
+ return d->requestedSize;
+}
+
+/*!
+ Returns the image that is currently associated with the specified
+ \a face of this cube map texture. The image may not have been
+ uploaded into the GL server yet. Uploads occur upon the next
+ call to bind().
+
+ \sa setImage()
+*/
+QImage QGLTextureCube::image(QGLTextureCube::Face face) const
+{
+ Q_D(const QGLTextureCube);
+ if (uint(face) >= 6)
+ return QImage();
+ if (face == 0)
+ return d->image;
+ else
+ return d->otherImages[face - 1];
+}
+
+/*!
+ Sets the \a image that is associated with this texture on the
+ specified \a face of the cube map. The image will be uploaded
+ into the GL server the next time bind() is called.
+
+ If setSize() or setImage() has been called previously, then \a image
+ will be scaled to size() when it is uploaded.
+
+ If \a image is null, then this function is equivalent to clearImage().
+
+ \sa image(), setSize(), copyImage()
+*/
+void QGLTextureCube::setImage
+ (QGLTextureCube::Face face, const QImage& image)
+{
+ Q_D(QGLTextureCube);
+ if (uint(face) >= 6)
+ return;
+ if (image.isNull()) {
+ // Don't change the imageGeneration, because we aren't actually
+ // changing the image in the GL server, only the client copy.
+ if (face == 0)
+ d->image = image;
+ else
+ d->otherImages[face - 1] = image;
+ } else {
+ if (!d->size.isValid())
+ setSize(image.size());
+ if (face == 0)
+ d->image = image;
+ else
+ d->otherImages[face - 1] = image;
+ ++(d->imageGeneration);
+ d->changedFaces |= (1 << face);
+ }
+}
+
+/*!
+ Clears the image() that is associated with this texture on the
+ specified \a face of the cube map. The GL texture will retain
+ its current value. This can be used to release client-side memory
+ that is no longer required once the image has been uploaded into
+ the GL server.
+
+ The following code will queue \c image to be uploaded as the
+ positive X face of the cube map, immediately force it to
+ be uploaded into the current GL context, and then clear the
+ client copy:
+
+ \code
+ texture.setImage(QGLTextureCube::PositiveX, image);
+ texture.bind();
+ texture.clearImage(QGLTextureCube::PositiveX);
+ \endcode
+
+ \sa image(), setImage()
+*/
+void QGLTextureCube::clearImage(QGLTextureCube::Face face)
+{
+ Q_D(QGLTextureCube);
+ if (face == 0)
+ d->image = QImage();
+ else
+ d->otherImages[face - 1] = QImage();
+}
+
+/*!
+ Copies the contents of \a image to \a offset in this texture
+ within the current GL context. The \a face parameter indicates
+ which face of the cube map should be altered.
+
+ Unlike setImage(), this function copies the image data to the
+ GL server immediately using \c{glTexSubImage2D()}. This is typically
+ used to update the contents of a texture after it has been created.
+
+ It is assumed that the application has already called bind() on
+ this texture to bind it to the current GL context.
+
+ If the texture has been created in multiple contexts, only the
+ texture identifier for the current context will be updated.
+
+ \sa setImage(), bind()
+*/
+void QGLTextureCube::copyImage
+ (QGLTextureCube::Face face, const QImage& image, const QPoint& offset)
+{
+ if (uint(face) >= 6)
+ return; // Invalid face number.
+ QImage img = QGLWidget::convertToGLFormat(image);
+ glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + int(face),
+ 0, offset.x(), offset.y(),
+ img.width(), img.height(), GL_RGBA,
+ GL_UNSIGNED_BYTE, img.bits());
+#if defined(QT_OPENGL_ES_2)
+ Q_D(QGLTextureCube);
+ if (d->bindOptions & QGLContext::MipmapBindOption)
+ glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+#endif
+}
+
+/*!
+ Returns the options to use when binding the image() to an OpenGL
+ context for the first time. The default options are
+ QGLContext::LinearFilteringBindOption |
+ QGLContext::InvertedYBindOption | QGLContext::MipmapBindOption.
+
+ \sa setBindOptions()
+*/
+QGLContext::BindOptions QGLTextureCube::bindOptions() const
+{
+ Q_D(const QGLTextureCube);
+ return d->bindOptions;
+}
+
+/*!
+ Sets the \a options to use when binding the image() to an
+ OpenGL context. If the image() has already been bound,
+ then changing the options will cause it to be recreated
+ from image() the next time bind() is called.
+
+ \sa bindOptions(), bind()
+*/
+void QGLTextureCube::setBindOptions(QGLContext::BindOptions options)
+{
+ Q_D(QGLTextureCube);
+ if (d->bindOptions != options) {
+ d->bindOptions = options;
+ ++(d->imageGeneration);
+ }
+}
+
+/*!
+ Returns the wrapping mode for horizontal texture co-ordinates.
+ The default value is QGL::Repeat.
+
+ \sa setHorizontalWrap(), verticalWrap()
+*/
+QGL::TextureWrap QGLTextureCube::horizontalWrap() const
+{
+ Q_D(const QGLTextureCube);
+ return d->horizontalWrap;
+}
+
+/*!
+ Sets the wrapping mode for horizontal texture co-ordinates to \a value.
+
+ If \a value is not supported by the OpenGL implementation, it will be
+ replaced with a value that is supported. If the application desires a
+ very specific \a value, it can call horizontalWrap() to check that
+ the specific value was actually set.
+
+ The \a value will not be applied to the texture in the GL
+ server until the next call to bind().
+
+ \sa horizontalWrap(), setVerticalWrap()
+*/
+void QGLTextureCube::setHorizontalWrap(QGL::TextureWrap value)
+{
+ Q_D(QGLTextureCube);
+ value = qt_gl_modify_texture_wrap(value);
+ if (d->horizontalWrap != value) {
+ d->horizontalWrap = value;
+ ++(d->parameterGeneration);
+ }
+}
+
+/*!
+ Returns the wrapping mode for vertical texture co-ordinates.
+ The default value is QGL::Repeat.
+
+ \sa setVerticalWrap(), horizontalWrap()
+*/
+QGL::TextureWrap QGLTextureCube::verticalWrap() const
+{
+ Q_D(const QGLTextureCube);
+ return d->verticalWrap;
+}
+
+/*!
+ Sets the wrapping mode for vertical texture co-ordinates to \a value.
+
+ If \a value is not supported by the OpenGL implementation, it will be
+ replaced with a value that is supported. If the application desires a
+ very specific \a value, it can call verticalWrap() to check that
+ the specific value was actually set.
+
+ The \a value will not be applied to the texture in the GL
+ server until the next call to bind().
+
+ \sa verticalWrap(), setHorizontalWrap()
+*/
+void QGLTextureCube::setVerticalWrap(QGL::TextureWrap value)
+{
+ Q_D(QGLTextureCube);
+ value = qt_gl_modify_texture_wrap(value);
+ if (d->verticalWrap != value) {
+ d->verticalWrap = value;
+ ++(d->parameterGeneration);
+ }
+}
+
+/*!
+ Binds this texture to the cube map texture target.
+
+ If this texture object is not associated with an identifier in
+ the current context, then a new identifier will be created,
+ and the face images will be uploaded into the GL server.
+
+ If setImage() or setSize() was called since the last upload,
+ then the face images will be re-uploaded to the GL server.
+
+ Returns false if the texture could not be bound for some reason.
+
+ \sa release(), textureId(), setImage()
+*/
+bool QGLTextureCube::bind() const
+{
+ Q_D(const QGLTextureCube);
+ return const_cast<QGLTextureCubePrivate *>(d)->bind(GL_TEXTURE_CUBE_MAP);
+}
+
+/*!
+ Releases the texture associated with the cube map texture target.
+ This is equivalent to \c{glBindTexture(GL_TEXTURE_CUBE_MAP, 0)}.
+
+ \sa bind()
+*/
+void QGLTextureCube::release()
+{
+ glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+}
+
+/*!
+ Returns the identifier associated with this texture object in
+ the current context.
+
+ Returns zero if the texture has not previously been bound to
+ the 2D texture target in the current context with bind().
+
+ \sa bind()
+*/
+GLuint QGLTextureCube::textureId() const
+{
+ Q_D(const QGLTextureCube);
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (!ctx)
+ return 0;
+ QGLTexture2DTextureInfo *info = d->infos;
+ while (info != 0 && info->tex.context() != ctx)
+ info = info->next;
+ return info ? info->tex.textureId() : 0;
+}
+
+/*!
+ Constructs a QGLTextureCube object that wraps the supplied literal
+ texture identifier \a id, with the dimensions specified by \a size.
+
+ The \a id is assumed to have been created by the application in
+ the current GL context, and it will be destroyed by the application
+ after the returned QGLTextureCube object is destroyed.
+
+ This function is intended for interfacing to existing code that
+ uses raw GL texture identifiers. The returned QGLTextureCube can
+ only be used with the current GL context.
+
+ \sa textureId()
+*/
+QGLTextureCube *QGLTextureCube::fromTextureId(GLuint id, const QSize& size)
+{
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (!id || !ctx)
+ return 0;
+
+ QGLTextureCube *texture = new QGLTextureCube();
+ if (!size.isNull())
+ texture->setSize(size);
+ QGLTexture2DTextureInfo *info = new QGLTexture2DTextureInfo
+ (ctx, id, texture->d_ptr->imageGeneration,
+ texture->d_ptr->parameterGeneration, true);
+ texture->d_ptr->infos = info;
+ return texture;
+}
+
+QT_END_NAMESPACE
diff --git a/src/threed/textures/qgltexturecube.h b/src/threed/textures/qgltexturecube.h
new file mode 100644
index 000000000..dfa445abc
--- /dev/null
+++ b/src/threed/textures/qgltexturecube.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLTEXTURECUBEMAP_H
+#define QGLTEXTURECUBEMAP_H
+
+#include "qglnamespace.h"
+#include <QtOpenGL/qgl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class QGLTextureCubePrivate;
+
+class Q_QT3D_EXPORT QGLTextureCube
+{
+public:
+ QGLTextureCube();
+ ~QGLTextureCube();
+
+ enum Face
+ {
+ PositiveX,
+ NegativeX,
+ PositiveY,
+ NegativeY,
+ PositiveZ,
+ NegativeZ
+ };
+
+ bool isNull() const;
+ bool hasAlphaChannel() const;
+
+ QSize size() const;
+ void setSize(const QSize& value);
+ QSize requestedSize() const;
+
+ QImage image(QGLTextureCube::Face face) const;
+ void setImage(QGLTextureCube::Face face, const QImage& image);
+ void clearImage(QGLTextureCube::Face face);
+
+ void copyImage(QGLTextureCube::Face face, const QImage& image, const QPoint& offset = QPoint(0, 0));
+
+ QGLContext::BindOptions bindOptions() const;
+ void setBindOptions(QGLContext::BindOptions options);
+
+ QGL::TextureWrap horizontalWrap() const;
+ void setHorizontalWrap(QGL::TextureWrap value);
+
+ QGL::TextureWrap verticalWrap() const;
+ void setVerticalWrap(QGL::TextureWrap value);
+
+ bool bind() const;
+ static void release();
+
+ GLuint textureId() const;
+
+ static QGLTextureCube *fromTextureId(GLuint id, const QSize& size);
+
+private:
+ QScopedPointer<QGLTextureCubePrivate> d_ptr;
+
+ Q_DISABLE_COPY(QGLTextureCube)
+ Q_DECLARE_PRIVATE(QGLTextureCube)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/threed/textures/qgltextureutils.cpp b/src/threed/textures/qgltextureutils.cpp
new file mode 100644
index 000000000..47c5de489
--- /dev/null
+++ b/src/threed/textures/qgltextureutils.cpp
@@ -0,0 +1,785 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgltextureutils_p.h"
+#include "qglext_p.h"
+#include <QtCore/qfile.h>
+
+QT_BEGIN_NAMESPACE
+
+QGL::TextureWrap qt_gl_modify_texture_wrap(QGL::TextureWrap value)
+{
+ switch (value) {
+#if defined(QT_OPENGL_ES)
+ case QGL::Clamp:
+ value = QGL::ClampToEdge;
+ break;
+#endif
+#if !defined(QT_OPENGL_ES)
+ case QGL::ClampToBorder:
+ if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_3)
+ == 0)
+ value = QGL::Clamp;
+ break;
+#else
+ case QGL::ClampToBorder:
+ value = QGL::ClampToEdge;
+ break;
+#endif
+#if !defined(QT_OPENGL_ES)
+ case QGL::ClampToEdge:
+ if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2)
+ == 0)
+ value = QGL::Clamp;
+ break;
+#endif
+#if !defined(QT_OPENGL_ES)
+ case QGL::MirroredRepeat:
+ if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_4)
+ == 0)
+ value = QGL::Repeat;
+ break;
+#elif !defined(QT_OPENGL_ES_2)
+ case QGL::MirroredRepeat:
+ value = QGL::Repeat;
+ break;
+#endif
+ default: break;
+ }
+ return value;
+}
+
+QGLTextureExtensions::QGLTextureExtensions(const QGLContext *ctx)
+ : npotTextures(false)
+ , generateMipmap(false)
+ , bgraTextureFormat(false)
+ , ddsTextureCompression(false)
+ , etc1TextureCompression(false)
+ , pvrtcTextureCompression(false)
+ , compressedTexImage2D(0)
+{
+ Q_UNUSED(ctx);
+ QGLExtensionChecker extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
+ if (extensions.match("GL_ARB_texture_non_power_of_two"))
+ npotTextures = true;
+ if (extensions.match("GL_SGIS_generate_mipmap"))
+ generateMipmap = true;
+ if (extensions.match("GL_EXT_bgra"))
+ bgraTextureFormat = true;
+ if (extensions.match("GL_EXT_texture_compression_s3tc"))
+ ddsTextureCompression = true;
+ if (extensions.match("GL_OES_compressed_ETC1_RGB8_texture"))
+ etc1TextureCompression = true;
+ if (extensions.match("GL_IMG_texture_compression_pvrtc"))
+ pvrtcTextureCompression = true;
+#if defined(QT_OPENGL_ES_2)
+ npotTextures = true;
+ generateMipmap = true;
+#endif
+#if !defined(QT_OPENGL_ES)
+ if (extensions.match("GL_ARB_texture_compression")) {
+ compressedTexImage2D = (q_glCompressedTexImage2DARB)
+ ctx->getProcAddress(QLatin1String("glCompressedTexImage2DARB"));
+ }
+#else
+ compressedTexImage2D = glCompressedTexImage2D;
+#endif
+}
+
+QGLTextureExtensions::~QGLTextureExtensions()
+{
+}
+
+Q_GLOBAL_STATIC(QGLResource<QGLTextureExtensions>, qt_gl_texture_extensions)
+
+QGLTextureExtensions *QGLTextureExtensions::extensions()
+{
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (!ctx)
+ return 0;
+ return qt_gl_texture_extensions()->value(ctx);
+}
+
+static void qt_gl_destroyTextureId(GLuint id)
+{
+ glDeleteTextures(1, &id);
+}
+
+QGLBoundTexture::QGLBoundTexture()
+ : m_resource(qt_gl_destroyTextureId)
+ , m_options(QGLContext::DefaultBindOption)
+ , m_hasAlpha(false)
+{
+}
+
+QGLBoundTexture::~QGLBoundTexture()
+{
+}
+
+// #define QGL_BIND_TEXTURE_DEBUG
+
+void QGLBoundTexture::startUpload(const QGLContext *ctx, GLenum target, const QSize &imageSize)
+{
+ Q_UNUSED(imageSize);
+
+ QGLTextureExtensions *extensions = QGLTextureExtensions::extensions();
+ if (!extensions)
+ return;
+
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf("QGLBoundTexture::startUpload(), imageSize=(%d,%d), options=%x\n",
+ imageSize.width(), imageSize.height(), int(m_options));
+ time.start();
+#endif
+
+#ifndef QT_NO_DEBUG
+ // Reset the gl error stack...
+ while (glGetError() != GL_NO_ERROR) ;
+#endif
+
+ // Create the texture id for the target, which should be one of
+ // GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP.
+ GLuint id = m_resource.id();
+ if (id) {
+ glBindTexture(target, 0); // Just in case texture is bound.
+ m_resource.destroy();
+ }
+ id = 0;
+ glGenTextures(1, &id);
+ glBindTexture(target, id);
+ m_resource.attach(ctx, id);
+
+ GLuint filtering = m_options & QGLContext::LinearFilteringBindOption ? GL_LINEAR : GL_NEAREST;
+
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - setting options (%d ms)\n", time.elapsed());
+#endif
+ q_glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filtering);
+
+ if (QGLContext::currentContext()->format().directRendering()
+ && extensions->generateMipmap
+ && (m_options & QGLContext::MipmapBindOption))
+ {
+#if !defined(QT_OPENGL_ES_2)
+ glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
+ q_glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
+#else
+ glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
+#endif
+ q_glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
+ m_options & QGLContext::LinearFilteringBindOption
+ ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST);
+ } else {
+ q_glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filtering);
+ m_options &= ~QGLContext::MipmapBindOption;
+ }
+}
+
+// map from Qt's ARGB endianness-dependent format to GL's big-endian RGBA layout
+static inline void qt_gl_byteSwapImage(QImage &img, GLenum pixel_type)
+{
+ const int width = img.width();
+ const int height = img.height();
+
+ if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV
+ || (pixel_type == GL_UNSIGNED_BYTE && QSysInfo::ByteOrder == QSysInfo::LittleEndian))
+ {
+ for (int i = 0; i < height; ++i) {
+ uint *p = (uint *) img.scanLine(i);
+ for (int x = 0; x < width; ++x)
+ p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
+ }
+ } else {
+ for (int i = 0; i < height; ++i) {
+ uint *p = (uint *) img.scanLine(i);
+ for (int x = 0; x < width; ++x)
+ p[x] = (p[x] << 8) | ((p[x] >> 24) & 0xff);
+ }
+ }
+}
+
+// #define QGL_BIND_TEXTURE_DEBUG
+
+void QGLBoundTexture::uploadFace
+ (GLenum target, const QImage &image, const QSize &scaleSize, GLenum format)
+{
+ GLenum internalFormat(format);
+
+ // Resolve the texture-related extensions for the current context.
+ QGLTextureExtensions *extensions = QGLTextureExtensions::extensions();
+ if (!extensions)
+ return;
+
+ // Adjust the image size for scaling and power of two.
+ QSize size = (!scaleSize.isEmpty() ? scaleSize : image.size());
+ if (!extensions->npotTextures)
+ size = QGL::nextPowerOfTwo(size);
+ QImage img(image);
+ if (size != image.size()) {
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - scaling up to %dx%d (%d ms) \n", size.width(), size.height(), time.elapsed());
+#endif
+ img = img.scaled(size);
+ }
+ m_size = size;
+
+ QImage::Format target_format = img.format();
+ bool premul = m_options & QGLContext::PremultipliedAlphaBindOption;
+ GLenum externalFormat;
+ GLuint pixel_type;
+ if (extensions->bgraTextureFormat) {
+ externalFormat = GL_BGRA;
+ if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2)
+ pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ else
+ pixel_type = GL_UNSIGNED_BYTE;
+ } else {
+ externalFormat = GL_RGBA;
+ pixel_type = GL_UNSIGNED_BYTE;
+ }
+
+ switch (target_format) {
+ case QImage::Format_ARGB32:
+ if (premul) {
+ img = img.convertToFormat(target_format = QImage::Format_ARGB32_Premultiplied);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - converting ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed());
+#endif
+ }
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ if (!premul) {
+ img = img.convertToFormat(target_format = QImage::Format_ARGB32);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - converting ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed());
+#endif
+ }
+ break;
+ case QImage::Format_RGB16:
+ pixel_type = GL_UNSIGNED_SHORT_5_6_5;
+ externalFormat = GL_RGB;
+ internalFormat = GL_RGB;
+ break;
+ case QImage::Format_RGB32:
+ break;
+ default:
+ if (img.hasAlphaChannel()) {
+ img = img.convertToFormat(premul
+ ? QImage::Format_ARGB32_Premultiplied
+ : QImage::Format_ARGB32);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - converting to 32-bit alpha format (%d ms)\n", time.elapsed());
+#endif
+ } else {
+ img = img.convertToFormat(QImage::Format_RGB32);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - converting to 32-bit (%d ms)\n", time.elapsed());
+#endif
+ }
+ }
+
+ if (m_options & QGLContext::InvertedYBindOption) {
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - flipping bits over y (%d ms)\n", time.elapsed());
+#endif
+ if (img.isDetached()) {
+ int ipl = img.bytesPerLine() / 4;
+ int h = img.height();
+ for (int y=0; y<h/2; ++y) {
+ int *a = (int *) img.scanLine(y);
+ int *b = (int *) img.scanLine(h - y - 1);
+ for (int x=0; x<ipl; ++x)
+ qSwap(a[x], b[x]);
+ }
+ } else {
+ // Create a new image and copy across. If we use the
+ // above in-place code then a full copy of the image is
+ // made before the lines are swapped, which processes the
+ // data twice. This version should only do it once.
+ img = img.mirrored();
+ }
+ }
+
+ if (externalFormat == GL_RGBA) {
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - doing byte swapping (%d ms)\n", time.elapsed());
+#endif
+ // The only case where we end up with a depth different from
+ // 32 in the switch above is for the RGB16 case, where we set
+ // the format to GL_RGB
+ Q_ASSERT(img.depth() == 32);
+ qt_gl_byteSwapImage(img, pixel_type);
+ }
+#ifdef QT_OPENGL_ES
+ // OpenGL/ES requires that the internal and external formats be
+ // identical.
+ internalFormat = externalFormat;
+#endif
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - uploading, image.format=%d, externalFormat=0x%x, internalFormat=0x%x, pixel_type=0x%x\n",
+ img.format(), externalFormat, internalFormat, pixel_type);
+#endif
+
+ const QImage &constRef = img; // to avoid detach in bits()...
+ glTexImage2D(target, 0, internalFormat, img.width(), img.height(), 0, externalFormat,
+ pixel_type, constRef.bits());
+
+ m_hasAlpha = (internalFormat != GL_RGB);
+}
+
+void QGLBoundTexture::createFace
+ (GLenum target, const QSize &size, GLenum format)
+{
+ glTexImage2D(target, 0, format, size.width(),
+ size.height(), 0, format, GL_UNSIGNED_BYTE, 0);
+ m_hasAlpha = (format != GL_RGB);
+}
+
+void QGLBoundTexture::finishUpload(GLenum target)
+{
+ Q_UNUSED(target);
+
+#if defined(QT_OPENGL_ES_2)
+ // OpenGL/ES 2.0 needs to generate mipmaps after all cubemap faces
+ // have been uploaded.
+ if (m_options & QGLContext::MipmapBindOption) {
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ printf(" - generating mipmaps (%d ms)\n", time.elapsed());
+#endif
+ glGenerateMipmap(target);
+ }
+#endif
+
+#ifndef QT_NO_DEBUG
+ GLenum error = glGetError();
+ if (error != GL_NO_ERROR) {
+ qWarning(" - texture upload failed, error code 0x%x, enum: %d (%x)\n", error, target, target);
+ }
+#endif
+
+#ifdef QGL_BIND_TEXTURE_DEBUG
+ static int totalUploadTime = 0;
+ totalUploadTime += time.elapsed();
+ printf(" - upload done in (%d ms) time=%d\n", time.elapsed(), totalUploadTime);
+#endif
+}
+
+// DDS format structure
+struct DDSFormat {
+ quint32 dwSize;
+ quint32 dwFlags;
+ quint32 dwHeight;
+ quint32 dwWidth;
+ quint32 dwLinearSize;
+ quint32 dummy1;
+ quint32 dwMipMapCount;
+ quint32 dummy2[11];
+ struct {
+ quint32 dummy3[2];
+ quint32 dwFourCC;
+ quint32 dummy4[5];
+ } ddsPixelFormat;
+};
+
+// compressed texture pixel formats
+#define FOURCC_DXT1 0x31545844
+#define FOURCC_DXT2 0x32545844
+#define FOURCC_DXT3 0x33545844
+#define FOURCC_DXT4 0x34545844
+#define FOURCC_DXT5 0x35545844
+
+#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+
+// PVR header format for container files that store textures compressed
+// with the ETC1, PVRTC2, and PVRTC4 encodings. Format information from the
+// PowerVR SDK at http://www.imgtec.com/powervr/insider/powervr-sdk.asp
+// "PVRTexTool Reference Manual, version 1.11f".
+struct PvrHeader
+{
+ quint32 headerSize;
+ quint32 height;
+ quint32 width;
+ quint32 mipMapCount;
+ quint32 flags;
+ quint32 dataSize;
+ quint32 bitsPerPixel;
+ quint32 redMask;
+ quint32 greenMask;
+ quint32 blueMask;
+ quint32 alphaMask;
+ quint32 magic;
+ quint32 surfaceCount;
+};
+
+#define PVR_MAGIC 0x21525650 // "PVR!" in little-endian
+
+#define PVR_FORMAT_MASK 0x000000FF
+#define PVR_FORMAT_PVRTC2 0x00000018
+#define PVR_FORMAT_PVRTC4 0x00000019
+#define PVR_FORMAT_ETC1 0x00000036
+
+#define PVR_HAS_MIPMAPS 0x00000100
+#define PVR_TWIDDLED 0x00000200
+#define PVR_NORMAL_MAP 0x00000400
+#define PVR_BORDER_ADDED 0x00000800
+#define PVR_CUBE_MAP 0x00001000
+#define PVR_FALSE_COLOR_MIPMAPS 0x00002000
+#define PVR_VOLUME_TEXTURE 0x00004000
+#define PVR_ALPHA_IN_TEXTURE 0x00008000
+#define PVR_VERTICAL_FLIP 0x00010000
+
+#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#endif
+
+#ifndef GL_ETC1_RGB8_OES
+#define GL_ETC1_RGB8_OES 0x8D64
+#endif
+
+bool QGLBoundTexture::canBindCompressedTexture
+ (const char *buf, int len, const char *format, bool *hasAlpha,
+ bool *isFlipped)
+{
+ if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) {
+ // Compressed texture loading only supported on little-endian
+ // systems such as x86 and ARM at the moment.
+ return false;
+ }
+ if (!format) {
+ // Auto-detect the format from the header.
+ if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) {
+ *hasAlpha = true;
+ *isFlipped = true;
+ return true;
+ } else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) {
+ const PvrHeader *pvrHeader =
+ reinterpret_cast<const PvrHeader *>(buf);
+ *hasAlpha = (pvrHeader->alphaMask != 0);
+ *isFlipped = ((pvrHeader->flags & PVR_VERTICAL_FLIP) != 0);
+ return true;
+ }
+ } else {
+ // Validate the format against the header.
+ if (!qstricmp(format, "DDS")) {
+ if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) {
+ *hasAlpha = true;
+ *isFlipped = true;
+ return true;
+ }
+ } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) {
+ if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) {
+ const PvrHeader *pvrHeader =
+ reinterpret_cast<const PvrHeader *>(buf);
+ *hasAlpha = (pvrHeader->alphaMask != 0);
+ *isFlipped = ((pvrHeader->flags & PVR_VERTICAL_FLIP) != 0);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool QGLBoundTexture::bindCompressedTexture
+ (const char *buf, int len, const char *format)
+{
+ if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) {
+ // Compressed texture loading only supported on little-endian
+ // systems such as x86 and ARM at the moment.
+ return false;
+ }
+#if !defined(QT_OPENGL_ES)
+ QGLTextureExtensions *extensions = QGLTextureExtensions::extensions();
+ if (!extensions)
+ return false;
+ if (!extensions->compressedTexImage2D) {
+ qWarning("QGLContext::bindTexture(): The GL implementation does "
+ "not support texture compression extensions.");
+ return false;
+ }
+#endif
+ if (!format) {
+ // Auto-detect the format from the header.
+ if (len >= 4 && !qstrncmp(buf, "DDS ", 4))
+ return bindCompressedTextureDDS(buf, len);
+ else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4))
+ return bindCompressedTexturePVR(buf, len);
+ } else {
+ // Validate the format against the header.
+ if (!qstricmp(format, "DDS")) {
+ if (len >= 4 && !qstrncmp(buf, "DDS ", 4))
+ return bindCompressedTextureDDS(buf, len);
+ } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) {
+ if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4))
+ return bindCompressedTexturePVR(buf, len);
+ }
+ }
+ return false;
+}
+
+bool QGLBoundTexture::bindCompressedTexture
+ (const QString& fileName, const char *format)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+ QByteArray contents = file.readAll();
+ file.close();
+ return bindCompressedTexture
+ (contents.constData(), contents.size(), format);
+}
+
+bool QGLBoundTexture::bindCompressedTextureDDS(const char *buf, int len)
+{
+ QGLTextureExtensions *extensions = QGLTextureExtensions::extensions();
+ if (!extensions)
+ return false;
+
+ // Bail out if the necessary extension is not present.
+ if (!extensions->ddsTextureCompression) {
+ qWarning("QGLBoundTexture::bindCompressedTextureDDS(): DDS texture compression is not supported.");
+ return false;
+ }
+
+ const DDSFormat *ddsHeader = reinterpret_cast<const DDSFormat *>(buf + 4);
+ if (!ddsHeader->dwLinearSize) {
+ qWarning("QGLBoundTexture::bindCompressedTextureDDS(): DDS image size is not valid.");
+ return false;
+ }
+
+ int blockSize = 16;
+ GLenum format;
+
+ switch(ddsHeader->ddsPixelFormat.dwFourCC) {
+ case FOURCC_DXT1:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ blockSize = 8;
+ break;
+ case FOURCC_DXT3:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ break;
+ case FOURCC_DXT5:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ break;
+ default:
+ qWarning("QGLBoundTexture::bindCompressedTextureDDS(): DDS image format not supported.");
+ return false;
+ }
+
+ const GLubyte *pixels =
+ reinterpret_cast<const GLubyte *>(buf + ddsHeader->dwSize + 4);
+
+ GLuint id = m_resource.id();
+ if (id) {
+ glBindTexture(GL_TEXTURE_2D, 0); // Just in case it is bound.
+ m_resource.destroy();
+ }
+ id = 0;
+ glGenTextures(1, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+ q_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ q_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ m_resource.attach(QGLContext::currentContext(), id);
+
+ int size;
+ int offset = 0;
+ int available = len - int(ddsHeader->dwSize + 4);
+ int w = ddsHeader->dwWidth;
+ int h = ddsHeader->dwHeight;
+
+ // load mip-maps
+ for (int i = 0; i < (int) ddsHeader->dwMipMapCount; ++i) {
+ if (w == 0) w = 1;
+ if (h == 0) h = 1;
+
+ size = ((w+3)/4) * ((h+3)/4) * blockSize;
+ if (size > available)
+ break;
+ extensions->compressedTexImage2D
+ (GL_TEXTURE_2D, i, format, w, h, 0, size, pixels + offset);
+ offset += size;
+ available -= size;
+
+ // half size for each mip-map level
+ w = w/2;
+ h = h/2;
+ }
+
+ // DDS images are not inverted.
+ m_options &= ~QGLContext::InvertedYBindOption;
+
+ m_size = QSize(ddsHeader->dwWidth, ddsHeader->dwHeight);
+ m_hasAlpha = false;
+ return true;
+}
+
+bool QGLBoundTexture::bindCompressedTexturePVR(const char *buf, int len)
+{
+ QGLTextureExtensions *extensions = QGLTextureExtensions::extensions();
+ if (!extensions)
+ return false;
+
+ // Determine which texture format we will be loading.
+ const PvrHeader *pvrHeader = reinterpret_cast<const PvrHeader *>(buf);
+ GLenum textureFormat;
+ quint32 minWidth, minHeight;
+ switch (pvrHeader->flags & PVR_FORMAT_MASK) {
+ case PVR_FORMAT_PVRTC2:
+ if (pvrHeader->alphaMask)
+ textureFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+ else
+ textureFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+ minWidth = 16;
+ minHeight = 8;
+ break;
+
+ case PVR_FORMAT_PVRTC4:
+ if (pvrHeader->alphaMask)
+ textureFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+ else
+ textureFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
+ minWidth = 8;
+ minHeight = 8;
+ break;
+
+ case PVR_FORMAT_ETC1:
+ textureFormat = GL_ETC1_RGB8_OES;
+ minWidth = 4;
+ minHeight = 4;
+ break;
+
+ default:
+ qWarning("QGLBoundTexture::bindCompressedTexturePVR(): PVR image format 0x%x not supported.", int(pvrHeader->flags & PVR_FORMAT_MASK));
+ return false;
+ }
+
+ // Bail out if the necessary extension is not present.
+ if (textureFormat == GL_ETC1_RGB8_OES) {
+ if (!extensions->etc1TextureCompression) {
+ qWarning("QGLBoundTexture::bindCompressedTexturePVR(): ETC1 texture compression is not supported.");
+ return false;
+ }
+ } else {
+ if (!extensions->pvrtcTextureCompression) {
+ qWarning("QGLBoundTexture::bindCompressedTexturePVR(): PVRTC texture compression is not supported.");
+ return false;
+ }
+ }
+
+ // Boundary check on the buffer size.
+ quint32 bufferSize = pvrHeader->headerSize + pvrHeader->dataSize;
+ if (bufferSize > quint32(len)) {
+ qWarning("QGLBoundTexture::bindCompressedTexturePVR(): PVR image size is not valid.");
+ return false;
+ }
+
+ // Create the texture.
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ GLuint id = m_resource.id();
+ if (id) {
+ glBindTexture(GL_TEXTURE_2D, 0); // Just in case it is bound.
+ m_resource.destroy();
+ }
+ id = 0;
+ glGenTextures(1, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+ m_resource.attach(QGLContext::currentContext(), id);
+ if (pvrHeader->mipMapCount) {
+ if ((m_options & QGLContext::LinearFilteringBindOption) != 0) {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ } else {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+ }
+ } else if ((m_options & QGLContext::LinearFilteringBindOption) != 0) {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ // Load the compressed mipmap levels.
+ const GLubyte *buffer =
+ reinterpret_cast<const GLubyte *>(buf + pvrHeader->headerSize);
+ bufferSize = pvrHeader->dataSize;
+ quint32 level = 0;
+ quint32 width = pvrHeader->width;
+ quint32 height = pvrHeader->height;
+ while (bufferSize > 0 && level <= pvrHeader->mipMapCount) {
+ quint32 size =
+ (qMax(width, minWidth) * qMax(height, minHeight) *
+ pvrHeader->bitsPerPixel) / 8;
+ if (size > bufferSize)
+ break;
+ extensions->compressedTexImage2D
+ (GL_TEXTURE_2D, GLint(level), textureFormat,
+ GLsizei(width), GLsizei(height), 0, GLsizei(size), buffer);
+ width /= 2;
+ height /= 2;
+ buffer += size;
+ ++level;
+ }
+
+ // Restore the default pixel alignment for later texture uploads.
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+
+ // Set the invert flag for the texture. The "vertical flip"
+ // flag in PVR is the opposite sense to our sense of inversion.
+ if ((pvrHeader->flags & PVR_VERTICAL_FLIP) != 0)
+ m_options &= ~QGLContext::InvertedYBindOption;
+ else
+ m_options |= QGLContext::InvertedYBindOption;
+
+ m_size = QSize(pvrHeader->width, pvrHeader->height);
+ m_hasAlpha = (pvrHeader->alphaMask != 0);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/threed/textures/qgltextureutils_p.h b/src/threed/textures/qgltextureutils_p.h
new file mode 100644
index 000000000..2dc7ea4c5
--- /dev/null
+++ b/src/threed/textures/qgltextureutils_p.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLTEXTUREUTILS_P_H
+#define QGLTEXTUREUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt 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.
+//
+
+#include <QtOpenGL/qgl.h>
+#include <QtCore/qdatetime.h>
+#include "qglnamespace.h"
+#include "qopenglfunctions.h"
+#include "qglsharedresource_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+#ifndef GL_UNSIGNED_SHORT_5_6_5
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#endif
+#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#endif
+#ifndef GL_TEXTURE_CUBE_MAP
+#define GL_TEXTURE_CUBE_MAP 0x8513
+#endif
+#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_X
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
+#endif
+#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
+#endif
+
+#ifndef GL_GENERATE_MIPMAP_SGIS
+#define GL_GENERATE_MIPMAP_SGIS 0x8191
+#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192
+#endif
+
+#if !defined(QT_OPENGL_ES)
+#define q_glTexParameteri(target,name,value) \
+ glTexParameteri((target), (name), int(value))
+#else
+#define q_glTexParameteri(target,name,value) \
+ glTexParameterf((target), (name), GLfloat(int(value)))
+#endif
+
+// Modify a wrapping mode to account for platform differences.
+QGL::TextureWrap qt_gl_modify_texture_wrap(QGL::TextureWrap value);
+
+typedef void (QT3D_GLF_APIENTRYP q_glCompressedTexImage2DARB)
+ (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *);
+
+class QGLTextureExtensions
+{
+public:
+ QGLTextureExtensions(const QGLContext *ctx);
+ ~QGLTextureExtensions();
+
+ int npotTextures : 1;
+ int generateMipmap : 1;
+ int bgraTextureFormat : 1;
+ int ddsTextureCompression : 1;
+ int etc1TextureCompression : 1;
+ int pvrtcTextureCompression : 1;
+ q_glCompressedTexImage2DARB compressedTexImage2D;
+
+ static QGLTextureExtensions *extensions();
+};
+
+class QGLBoundTexture
+{
+public:
+ QGLBoundTexture();
+ ~QGLBoundTexture();
+
+ const QGLContext *context() const { return m_resource.context(); }
+
+ GLuint textureId() const { return m_resource.id(); }
+ void setTextureId(const QGLContext *ctx, GLuint id)
+ { m_resource.attach(ctx, id); }
+ void clearId() { m_resource.clearId(); }
+
+ QGLContext::BindOptions options() const { return m_options; }
+ void setOptions(QGLContext::BindOptions options) { m_options = options; }
+
+ QSize size() const { return m_size; }
+ bool hasAlpha() const { return m_hasAlpha; }
+
+ void startUpload(const QGLContext *ctx, GLenum target, const QSize &imageSize);
+ void uploadFace(GLenum target, const QImage &image, const QSize &scaleSize,
+ GLenum format = GL_RGBA);
+ void createFace(GLenum target, const QSize &size, GLenum format = GL_RGBA);
+ void finishUpload(GLenum target);
+
+ static bool canBindCompressedTexture
+ (const char *buf, int len, const char *format, bool *hasAlpha,
+ bool *isFlipped);
+ bool bindCompressedTexture
+ (const QString& fileName, const char *format = 0);
+ bool bindCompressedTexture
+ (const char *buf, int len, const char *format = 0);
+ bool bindCompressedTextureDDS(const char *buf, int len);
+ bool bindCompressedTexturePVR(const char *buf, int len);
+
+private:
+ QGLSharedResource m_resource;
+ QGLContext::BindOptions m_options;
+ QSize m_size;
+ bool m_hasAlpha;
+ QTime time;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/threed/textures/textures.pri b/src/threed/textures/textures.pri
new file mode 100644
index 000000000..c17c1531e
--- /dev/null
+++ b/src/threed/textures/textures.pri
@@ -0,0 +1,15 @@
+INCLUDEPATH += $$PWD
+VPATH += $$PWD
+HEADERS += \
+ qgltexture2d.h \
+ qgltexturecube.h \
+ qareaallocator.h
+SOURCES += \
+ qareaallocator.cpp \
+ qglsharedresource.cpp \
+ qgltexture2d.cpp \
+ qgltexturecube.cpp \
+ qgltextureutils.cpp
+PRIVATE_HEADERS += \
+ qglsharedresource_p.h \
+ qgltexture2d_p.h