1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #define GL_GLEXT_PROTOTYPES
44 #include <private/qsgtexture_p.h>
45 #include <qglfunctions.h>
46 #include <private/qsgcontext_p.h>
51 inline static bool isPowerOfTwo(int x)
57 QSGTexturePrivate::QSGTexturePrivate()
59 , filteringChanged(false)
60 , horizontalWrap(QSGTexture::ClampToEdge)
61 , verticalWrap(QSGTexture::ClampToEdge)
62 , mipmapMode(QSGTexture::None)
63 , filterMode(QSGTexture::Nearest)
68 static int qt_texture_count = 0;
70 static void qt_print_texture_count()
72 qDebug("Number of leaked textures: %i", qt_texture_count);
73 qt_texture_count = -1;
79 QSGTexture::QSGTexture()
80 : QObject(*(new QSGTexturePrivate))
84 static bool atexit_registered = false;
85 if (!atexit_registered) {
86 atexit(qt_print_texture_count);
87 atexit_registered = true;
92 QSGTexture::~QSGTexture()
96 if (qt_texture_count < 0)
97 qDebug("Material destroyed after qt_print_texture_count() was called.");
103 \fn void QSGTexture::setImage(const QImage &image)
105 This function may be calld from arbitrary an arbitrary thread and may not
111 \fn void QSGTexture::bind()
113 Call this function to bind this texture to the current texture
116 Binding a texture may also include uploading the texture data from
117 a previously set QImage.
120 void QSGTexture::removeFromAtlas()
122 // default textures are not in atlasses, so do nothing...
126 Returns weither this texture is part of an atlas or not.
128 The default implementation returns false.
130 bool QSGTexture::isAtlasTexture() const
137 Returns the rectangle inside textureSize() that this texture
138 represents in normalized coordinates.
140 The default implementation returns a rect at position (0, 0) with
141 width and height of 1.
143 QRectF QSGTexture::textureSubRect() const
145 return QRectF(0, 0, 1, 1);
149 \fn bool QSGTexture::hasMipmaps() const
151 Returns true if the texture data contains mipmap levels.
156 Sets the mipmap sampling mode to be used for the upcoming bind() call to \a filter.
158 Setting the mipmap filtering has no effect it the texture does not have mipmaps.
162 void QSGTexture::setMipmapFiltering(Filtering filter)
165 if (d->mipmapMode != (uint) filter) {
166 d->mipmapMode = filter;
167 d->filteringChanged = true;
172 Returns whether mipmapping should be used when sampling from this texture.
174 QSGTexture::Filtering QSGTexture::mipmapFiltering() const
176 return (QSGTexture::Filtering) d_func()->mipmapMode;
181 Sets the sampling mode to be used for the upcoming bind() call to \a filter.
183 void QSGTexture::setFiltering(QSGTexture::Filtering filter)
186 if (d->filterMode != (uint) filter) {
187 d->filterMode = filter;
188 d->filteringChanged = true;
192 QSGTexture::Filtering QSGTexture::filtering() const
194 return (QSGTexture::Filtering) d_func()->filterMode;
200 Sets the horizontal wrap mode to be used for the upcoming bind() call to \a hwrap
203 void QSGTexture::setHorizontalWrapMode(WrapMode hwrap)
206 if ((uint) hwrap != d->horizontalWrap) {
207 d->horizontalWrap = hwrap;
208 d->wrapChanged = true;
212 QSGTexture::WrapMode QSGTexture::horizontalWrapMode() const
214 return (QSGTexture::WrapMode) d_func()->horizontalWrap;
219 void QSGTexture::setVerticalWrapMode(WrapMode vwrap)
222 if ((uint) vwrap != d->verticalWrap) {
223 d->verticalWrap = vwrap;
224 d->wrapChanged = true;
228 QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
230 return (QSGTexture::WrapMode) d_func()->verticalWrap;
235 Update the texture state to match the filtering, mipmap and wrap options
238 If \a force is true, all properties will be updated regardless of weither
239 they have changed or not.
241 void QSGTexture::updateBindOptions(bool force)
244 if (force || d->filteringChanged) {
245 bool linear = d->filterMode == Linear;
246 GLint minFilter = linear ? GL_LINEAR : GL_NEAREST;
247 GLint magFilter = linear ? GL_LINEAR : GL_NEAREST;
250 if (d->mipmapMode == Nearest)
251 minFilter = linear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST;
252 else if (d->mipmapMode == Linear)
253 minFilter = linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR;
255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
257 d->filteringChanged = false;
260 if (force || d->wrapChanged) {
261 #if !defined(QT_NO_DEBUG) && defined(QT_OPENGL_ES_2)
262 if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat) {
263 bool npotSupported = QGLContext::currentContext()->functions()->hasOpenGLFeature(QGLFunctions::NPOTTextures);
264 QSize size = textureSize();
265 bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
266 if (!npotSupported && isNpot)
267 qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures.");
270 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, d->horizontalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
271 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
272 d->wrapChanged = false;
276 QSGPlainTexture::QSGPlainTexture()
280 , m_has_mipmaps(false)
281 , m_dirty_bind_options(false)
282 , m_owns_texture(true)
283 , m_mipmaps_generated(false)
288 QSGPlainTexture::~QSGPlainTexture()
290 if (m_texture_id && m_owns_texture)
291 glDeleteTextures(1, &m_texture_id);
295 static void swizzleBGRAToRGBA(QImage *image)
297 const int width = image->width();
298 const int height = image->height();
299 for (int i = 0; i < height; ++i) {
300 uint *p = (uint *) image->scanLine(i);
301 for (int x = 0; x < width; ++x)
302 p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
307 void QSGPlainTexture::setImage(const QImage &image)
309 m_image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
311 swizzleBGRAToRGBA(&m_image);
314 m_texture_size = image.size();
315 m_has_alpha = image.hasAlphaChannel();
316 m_dirty_texture = true;
317 m_dirty_bind_options = true;
320 void QSGPlainTexture::setTextureId(int id)
322 if (m_texture_id && m_owns_texture)
323 glDeleteTextures(1, &m_texture_id);
326 m_dirty_texture = false;
327 m_dirty_bind_options = true;
329 m_mipmaps_generated = false;
332 void QSGPlainTexture::setHasMipmaps(bool mm)
335 m_mipmaps_generated = false;
339 void QSGPlainTexture::bind()
341 if (!m_dirty_texture) {
342 glBindTexture(GL_TEXTURE_2D, m_texture_id);
343 if (m_has_mipmaps && !m_mipmaps_generated) {
344 const QGLContext *ctx = QGLContext::currentContext();
345 ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
346 m_mipmaps_generated = true;
348 updateBindOptions(m_dirty_bind_options);
349 m_dirty_bind_options = false;
353 m_dirty_texture = false;
355 if (m_texture_id && m_owns_texture)
356 glDeleteTextures(1, &m_texture_id);
358 if (m_image.isNull()) {
360 m_texture_size = QSize();
361 m_has_mipmaps = false;
366 glGenTextures(1, &m_texture_id);
367 glBindTexture(GL_TEXTURE_2D, m_texture_id);
369 // ### TODO: check for out-of-memory situations...
370 int w = m_image.width();
371 int h = m_image.height();
374 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_image.constBits());
376 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, m_image.constBits());
380 const QGLContext *ctx = QGLContext::currentContext();
381 ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
382 m_mipmaps_generated = true;
385 m_texture_size = QSize(w, h);
386 m_texture_rect = QRectF(0, 0, 1, 1);
388 updateBindOptions(m_dirty_bind_options);
389 m_dirty_bind_options = false;
394 \class QSGDynamicTexture
395 \brief The QSGDynamicTexture class serves as a baseclass for dynamically changing textures,
396 such as content that is rendered to FBO's.
398 To update the content of the texture, call updateTexture() explicitly. Simply calling bind()
399 will not update the texture.
404 \fn bool QSGDynamicTexture::updateTexture()
406 Call this function to explicitely update the dynamic texture. Calling bind() will bind
407 the content that was previously updated.
409 The function returns true if the texture was changed as a resul of the update; otherwise