c82f214cc8adc47b90e397fa29fa1d13054521a1
[profile/ivi/qtdeclarative.git] / src / declarative / scenegraph / util / qsgtexture.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
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
14 ** this package.
15 **
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.
23 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #define GL_GLEXT_PROTOTYPES
43
44 #include <private/qsgtexture_p.h>
45 #include <qglfunctions.h>
46 #include <private/qsgcontext_p.h>
47 #include <qthread.h>
48
49 QT_BEGIN_NAMESPACE
50
51 inline static bool isPowerOfTwo(int x)
52 {
53     // Assumption: x >= 1
54     return x == (x & -x);
55 }
56
57 QSGTexturePrivate::QSGTexturePrivate()
58     : wrapChanged(false)
59     , filteringChanged(false)
60     , horizontalWrap(QSGTexture::ClampToEdge)
61     , verticalWrap(QSGTexture::ClampToEdge)
62     , mipmapMode(QSGTexture::None)
63     , filterMode(QSGTexture::Nearest)
64 {
65 }
66
67 #ifndef QT_NO_DEBUG
68 static int qt_texture_count = 0;
69
70 static void qt_print_texture_count()
71 {
72     qDebug("Number of leaked textures: %i", qt_texture_count);
73     qt_texture_count = -1;
74 }
75 #endif
76
77
78
79 QSGTexture::QSGTexture()
80     : QObject(*(new QSGTexturePrivate))
81 {
82 #ifndef QT_NO_DEBUG
83     ++qt_texture_count;
84     static bool atexit_registered = false;
85     if (!atexit_registered) {
86         atexit(qt_print_texture_count);
87         atexit_registered = true;
88     }
89 #endif
90 }
91
92 QSGTexture::~QSGTexture()
93 {
94 #ifndef QT_NO_DEBUG
95     --qt_texture_count;
96     if (qt_texture_count < 0)
97         qDebug("Material destroyed after qt_print_texture_count() was called.");
98 #endif
99 }
100
101
102 /*!
103     \fn void QSGTexture::setImage(const QImage &image)
104
105     This function may be calld from arbitrary an arbitrary thread and may not
106     use GL calls.
107  */
108
109
110 /*!
111     \fn void QSGTexture::bind()
112
113     Call this function to bind this texture to the current texture
114     target.
115
116     Binding a texture may also include uploading the texture data from
117     a previously set QImage.
118  */
119
120 void QSGTexture::removeFromAtlas()
121 {
122     // default textures are not in atlasses, so do nothing...
123 }
124
125 /*!
126     Returns weither this texture is part of an atlas or not.
127
128     The default implementation returns false.
129  */
130 bool QSGTexture::isAtlasTexture() const
131 {
132     return false;
133 }
134
135
136 /*!
137     Returns the rectangle inside textureSize() that this texture
138     represents in normalized coordinates.
139
140     The default implementation returns a rect at position (0, 0) with
141     width and height of 1.
142  */
143 QRectF QSGTexture::textureSubRect() const
144 {
145     return QRectF(0, 0, 1, 1);
146 }
147
148 /*!
149     \fn bool QSGTexture::hasMipmaps() const
150
151     Returns true if the texture data contains mipmap levels.
152  */
153
154
155 /*!
156     Sets the mipmap sampling mode to be used for the upcoming bind() call to \a filter.
157
158     Setting the mipmap filtering has no effect it the texture does not have mipmaps.
159
160     \sa hasMipmaps()
161  */
162 void QSGTexture::setMipmapFiltering(Filtering filter)
163 {
164     Q_D(QSGTexture);
165     if (d->mipmapMode != (uint) filter) {
166         d->mipmapMode = filter;
167         d->filteringChanged = true;
168     }
169 }
170
171 /*!
172     Returns whether mipmapping should be used when sampling from this texture.
173  */
174 QSGTexture::Filtering QSGTexture::mipmapFiltering() const
175 {
176     return (QSGTexture::Filtering) d_func()->mipmapMode;
177 }
178
179
180 /*!
181     Sets the sampling mode to be used for the upcoming bind() call to \a filter.
182  */
183 void QSGTexture::setFiltering(QSGTexture::Filtering filter)
184 {
185     Q_D(QSGTexture);
186     if (d->filterMode != (uint) filter) {
187         d->filterMode = filter;
188         d->filteringChanged = true;
189     }
190 }
191
192 QSGTexture::Filtering QSGTexture::filtering() const
193 {
194     return (QSGTexture::Filtering) d_func()->filterMode;
195 }
196
197
198
199 /*!
200     Sets the horizontal wrap mode to be used for the upcoming bind() call to \a hwrap
201  */
202
203 void QSGTexture::setHorizontalWrapMode(WrapMode hwrap)
204 {
205     Q_D(QSGTexture);
206     if ((uint) hwrap != d->horizontalWrap) {
207         d->horizontalWrap = hwrap;
208         d->wrapChanged = true;
209     }
210 }
211
212 QSGTexture::WrapMode QSGTexture::horizontalWrapMode() const
213 {
214     return (QSGTexture::WrapMode) d_func()->horizontalWrap;
215 }
216
217
218
219 void QSGTexture::setVerticalWrapMode(WrapMode vwrap)
220 {
221     Q_D(QSGTexture);
222     if ((uint) vwrap != d->verticalWrap) {
223         d->verticalWrap = vwrap;
224         d->wrapChanged = true;
225     }
226 }
227
228 QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
229 {
230     return (QSGTexture::WrapMode) d_func()->verticalWrap;
231 }
232
233
234 /*!
235     Update the texture state to match the filtering, mipmap and wrap options
236     currently set.
237
238     If \a force is true, all properties will be updated regardless of weither
239     they have changed or not.
240  */
241 void QSGTexture::updateBindOptions(bool force)
242 {
243     Q_D(QSGTexture);
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;
248
249         if (hasMipmaps()) {
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;
254         }
255         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
256         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
257         d->filteringChanged = false;
258     }
259
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.");
268         }
269 #endif
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;
273     }
274 }
275
276 QSGPlainTexture::QSGPlainTexture()
277     : QSGTexture()
278     , m_texture_id(0)
279     , m_has_alpha(false)
280     , m_has_mipmaps(false)
281     , m_dirty_bind_options(false)
282     , m_owns_texture(true)
283     , m_mipmaps_generated(false)
284 {
285 }
286
287
288 QSGPlainTexture::~QSGPlainTexture()
289 {
290     if (m_texture_id && m_owns_texture)
291         glDeleteTextures(1, &m_texture_id);
292 }
293
294 #ifdef QT_OPENGL_ES
295 static void swizzleBGRAToRGBA(QImage *image)
296 {
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);
303     }
304 }
305 #endif
306
307 void QSGPlainTexture::setImage(const QImage &image)
308 {
309     m_image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
310 #ifdef QT_OPENGL_ES
311     swizzleBGRAToRGBA(&m_image);
312 #endif
313
314     m_texture_size = image.size();
315     m_has_alpha = image.hasAlphaChannel();
316     m_dirty_texture = true;
317     m_dirty_bind_options = true;
318  }
319
320 void QSGPlainTexture::setTextureId(int id)
321 {
322     if (m_texture_id && m_owns_texture)
323         glDeleteTextures(1, &m_texture_id);
324
325     m_texture_id = id;
326     m_dirty_texture = false;
327     m_dirty_bind_options = true;
328     m_image = QImage();
329     m_mipmaps_generated = false;
330 }
331
332 void QSGPlainTexture::setHasMipmaps(bool mm)
333 {
334     m_has_mipmaps = mm;
335     m_mipmaps_generated = false;
336 }
337
338
339 void QSGPlainTexture::bind()
340 {
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;
347         }
348         updateBindOptions(m_dirty_bind_options);
349         m_dirty_bind_options = false;
350         return;
351     }
352
353     m_dirty_texture = false;
354
355     if (m_texture_id && m_owns_texture)
356         glDeleteTextures(1, &m_texture_id);
357
358     if (m_image.isNull()) {
359         m_texture_id = 0;
360         m_texture_size = QSize();
361         m_has_mipmaps = false;
362         m_has_alpha = false;
363         return;
364     }
365
366     glGenTextures(1, &m_texture_id);
367     glBindTexture(GL_TEXTURE_2D, m_texture_id);
368
369     // ### TODO: check for out-of-memory situations...
370     int w = m_image.width();
371     int h = m_image.height();
372
373 #ifdef QT_OPENGL_ES
374     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_image.constBits());
375 #else
376     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, m_image.constBits());
377 #endif
378
379     if (m_has_mipmaps) {
380         const QGLContext *ctx = QGLContext::currentContext();
381         ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
382         m_mipmaps_generated = true;
383     }
384
385     m_texture_size = QSize(w, h);
386     m_texture_rect = QRectF(0, 0, 1, 1);
387
388     updateBindOptions(m_dirty_bind_options);
389     m_dirty_bind_options = false;
390 }
391
392
393 /*!
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.
397
398     To update the content of the texture, call updateTexture() explicitly. Simply calling bind()
399     will not update the texture.
400  */
401
402
403 /*!
404     \fn bool QSGDynamicTexture::updateTexture()
405
406     Call this function to explicitely update the dynamic texture. Calling bind() will bind
407     the content that was previously updated.
408
409     The function returns true if the texture was changed as a resul of the update; otherwise
410     returns false.
411  */
412
413
414
415 QT_END_NAMESPACE