Enable mipmapping for QSGPaintedItem's texture.
[profile/ivi/qtdeclarative.git] / src / declarative / scenegraph / util / qsgpainternode.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 #include "qsgpainternode_p.h"
43
44 #include "qsgpainteditem.h"
45 #include <private/qsgcontext_p.h>
46 #include <qglframebufferobject.h>
47 #include <qglfunctions.h>
48 #include <qmath.h>
49
50 QT_BEGIN_NAMESPACE
51
52 #define QT_MINIMUM_FBO_SIZE 64
53
54 static inline int qt_next_power_of_two(int v)
55 {
56     v--;
57     v |= v >> 1;
58     v |= v >> 2;
59     v |= v >> 4;
60     v |= v >> 8;
61     v |= v >> 16;
62     ++v;
63     return v;
64 }
65
66 QSGPainterTexture::QSGPainterTexture()
67     : QSGPlainTexture()
68 {
69
70 }
71
72 void QSGPainterTexture::bind()
73 {
74     if (m_dirty_rect.isNull() || m_texture_id == 0) {
75         QSGPlainTexture::bind();
76     } else {
77         glBindTexture(GL_TEXTURE_2D, m_texture_id);
78
79         QImage subImage = m_image.copy(m_dirty_rect);
80
81         int w = m_dirty_rect.width();
82         int h = m_dirty_rect.height();
83
84 #ifdef QT_OPENGL_ES
85         glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h,
86                         GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits());
87 #else
88         glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h,
89                         GL_BGRA, GL_UNSIGNED_BYTE, subImage.constBits());
90 #endif
91
92         m_dirty_texture = false;
93         m_dirty_bind_options = false;
94     }
95     m_dirty_rect = QRect();
96 }
97
98 QSGPainterNode::QSGPainterNode(QSGPaintedItem *item)
99     : QSGGeometryNode()
100     , m_preferredRenderTarget(QSGPaintedItem::Image)
101     , m_actualRenderTarget(QSGPaintedItem::Image)
102     , m_item(item)
103     , m_fbo(0)
104     , m_multisampledFbo(0)
105     , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
106     , m_texture(0)
107     , m_size(1, 1)
108     , m_dirtyContents(false)
109     , m_opaquePainting(false)
110     , m_linear_filtering(false)
111     , m_mipmapping(false)
112     , m_smoothPainting(false)
113     , m_extensionsChecked(false)
114     , m_multisamplingSupported(false)
115     , m_fillColor(Qt::transparent)
116     , m_contentsScale(1.0)
117     , m_dirtyGeometry(false)
118     , m_dirtyRenderTarget(false)
119     , m_dirtyTexture(false)
120 {
121     setMaterial(&m_materialO);
122     setOpaqueMaterial(&m_material);
123     setGeometry(&m_geometry);
124     setFlag(UsePreprocess);
125 }
126
127 QSGPainterNode::~QSGPainterNode()
128 {
129     delete m_texture;
130     delete m_fbo;
131     delete m_multisampledFbo;
132 }
133
134 void QSGPainterNode::preprocess()
135 {
136     if (!m_dirtyContents)
137         return;
138
139     QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect;
140
141     QPainter painter;
142     if (m_actualRenderTarget == QSGPaintedItem::Image)
143         painter.begin(&m_image);
144     else if (m_multisampledFbo)
145         painter.begin(m_multisampledFbo);
146     else
147         painter.begin(m_fbo);
148
149     if (m_smoothPainting) {
150         painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing
151                                | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
152     }
153
154     painter.scale(m_contentsScale, m_contentsScale);
155
156     QRect sclip(qFloor(dirtyRect.x()/m_contentsScale),
157                 qFloor(dirtyRect.y()/m_contentsScale),
158                 qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)),
159                 qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale)));
160
161     if (!m_dirtyRect.isNull())
162         painter.setClipRect(sclip);
163
164     painter.setCompositionMode(QPainter::CompositionMode_Source);
165     painter.fillRect(sclip, m_fillColor);
166     painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
167
168     m_item->paint(&painter);
169     painter.end();
170
171     if (m_actualRenderTarget == QSGPaintedItem::Image) {
172         m_texture->setImage(m_image);
173         m_texture->setDirtyRect(dirtyRect);
174     } else if (m_multisampledFbo) {
175         QGLFramebufferObject::blitFramebuffer(m_fbo, dirtyRect, m_multisampledFbo, dirtyRect);
176     }
177
178     m_dirtyContents = false;
179     m_dirtyRect = QRect();
180 }
181
182 void QSGPainterNode::update()
183 {
184     if (m_dirtyRenderTarget)
185         updateRenderTarget();
186     if (m_dirtyGeometry)
187         updateGeometry();
188     if (m_dirtyTexture)
189         updateTexture();
190
191     m_dirtyGeometry = false;
192     m_dirtyRenderTarget = false;
193     m_dirtyTexture = false;
194 }
195
196 void QSGPainterNode::updateTexture()
197 {
198     m_texture->setHasMipmaps(m_mipmapping);
199     m_texture->setHasAlphaChannel(!m_opaquePainting);
200     m_material.setTexture(m_texture);
201     m_materialO.setTexture(m_texture);
202
203     markDirty(DirtyMaterial);
204 }
205
206 void QSGPainterNode::updateGeometry()
207 {
208     QRectF source;
209     if (m_actualRenderTarget == QSGPaintedItem::Image)
210         source = QRectF(0, 0, 1, 1);
211     else
212         source = QRectF(0, 1, qreal(m_size.width()) / m_fboSize.width(), qreal(-m_size.height()) / m_fboSize.height());
213     QSGGeometry::updateTexturedRectGeometry(&m_geometry,
214                                             QRectF(0, 0, m_size.width(), m_size.height()),
215                                             source);
216     markDirty(DirtyGeometry);
217 }
218
219 void QSGPainterNode::updateRenderTarget()
220 {
221     if (!m_extensionsChecked) {
222         QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
223         m_multisamplingSupported = extensions.contains("GL_EXT_framebuffer_multisample")
224                 && extensions.contains("GL_EXT_framebuffer_blit");
225         m_extensionsChecked = true;
226     }
227
228     m_dirtyContents = true;
229
230     QSGPaintedItem::RenderTarget oldTarget = m_actualRenderTarget;
231     if (m_preferredRenderTarget == QSGPaintedItem::Image) {
232         m_actualRenderTarget = QSGPaintedItem::Image;
233     } else {
234         if (!m_multisamplingSupported && m_smoothPainting)
235             m_actualRenderTarget = QSGPaintedItem::Image;
236         else
237             m_actualRenderTarget = QSGPaintedItem::FramebufferObject;
238     }
239     if (oldTarget != m_actualRenderTarget) {
240         m_image = QImage();
241         delete m_fbo;
242         delete m_multisampledFbo;
243         m_fbo = m_multisampledFbo = 0;
244     }
245
246     if (m_actualRenderTarget == QSGPaintedItem::FramebufferObject) {
247         const QGLContext *ctx = QGLContext::currentContext();
248         if (m_fbo && !m_dirtyGeometry && (!ctx->format().sampleBuffers() || !m_multisamplingSupported))
249             return;
250
251         if (m_fboSize.isEmpty())
252             updateFBOSize();
253
254         delete m_fbo;
255         delete m_multisampledFbo;
256         m_fbo = m_multisampledFbo = 0;
257
258         if (m_smoothPainting && ctx->format().sampleBuffers() && m_multisamplingSupported) {
259             {
260                 QGLFramebufferObjectFormat format;
261                 format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
262                 format.setSamples(ctx->format().samples());
263                 m_multisampledFbo = new QGLFramebufferObject(m_fboSize, format);
264             }
265             {
266                 QGLFramebufferObjectFormat format;
267                 format.setAttachment(QGLFramebufferObject::NoAttachment);
268                 m_fbo = new QGLFramebufferObject(m_fboSize, format);
269             }
270         } else {
271             QGLFramebufferObjectFormat format;
272             format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
273             m_fbo = new QGLFramebufferObject(m_fboSize, format);
274         }
275     } else {
276         if (!m_image.isNull() && !m_dirtyGeometry)
277             return;
278
279         m_image = QImage(m_size, QImage::Format_ARGB32_Premultiplied);
280         m_image.fill(Qt::transparent);
281     }
282
283     QSGPainterTexture *texture = new QSGPainterTexture;
284     if (m_actualRenderTarget == QSGPaintedItem::Image) {
285         texture->setOwnsTexture(true);
286         texture->setTextureSize(m_size);
287     } else {
288         texture->setTextureId(m_fbo->texture());
289         texture->setOwnsTexture(false);
290         texture->setTextureSize(m_fboSize);
291     }
292
293     if (m_texture)
294         delete m_texture;
295
296     texture->setTextureSize(m_size);
297     m_texture = texture;
298 }
299
300 void QSGPainterNode::updateFBOSize()
301 {
302     int fboWidth = qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(m_size.width()));
303     int fboHeight = qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(m_size.height()));
304     m_fboSize = QSize(fboWidth, fboHeight);
305 }
306
307 void QSGPainterNode::setPreferredRenderTarget(QSGPaintedItem::RenderTarget target)
308 {
309     if (m_preferredRenderTarget == target)
310         return;
311
312     m_preferredRenderTarget = target;
313
314     m_dirtyRenderTarget = true;
315     m_dirtyGeometry = true;
316     m_dirtyTexture = true;
317 }
318
319 void QSGPainterNode::setSize(const QSize &size)
320 {
321     if (size == m_size)
322         return;
323
324     m_size = size;
325     updateFBOSize();
326
327     if (m_fbo)
328         m_dirtyRenderTarget = m_fbo->size() != m_fboSize || m_dirtyRenderTarget;
329     else
330         m_dirtyRenderTarget = true;
331     m_dirtyGeometry = true;
332     m_dirtyTexture = true;
333 }
334
335 void QSGPainterNode::setDirty(bool d, const QRect &dirtyRect)
336 {
337     m_dirtyContents = d;
338     m_dirtyRect = dirtyRect;
339
340     markDirty(DirtyMaterial);
341 }
342
343 void QSGPainterNode::setOpaquePainting(bool opaque)
344 {
345     if (opaque == m_opaquePainting)
346         return;
347
348     m_opaquePainting = opaque;
349     m_dirtyTexture = true;
350 }
351
352 void QSGPainterNode::setLinearFiltering(bool linearFiltering)
353 {
354     if (linearFiltering == m_linear_filtering)
355         return;
356
357     m_linear_filtering = linearFiltering;
358
359     m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
360     m_materialO.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
361     markDirty(DirtyMaterial);
362 }
363
364 void QSGPainterNode::setMipmapping(bool mipmapping)
365 {
366     if (mipmapping == m_mipmapping)
367         return;
368
369     m_mipmapping = mipmapping;
370     m_material.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
371     m_materialO.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
372     m_dirtyTexture = true;
373 }
374
375 void QSGPainterNode::setSmoothPainting(bool s)
376 {
377     if (s == m_smoothPainting)
378         return;
379
380     m_smoothPainting = s;
381     m_dirtyRenderTarget = true;
382 }
383
384 void QSGPainterNode::setFillColor(const QColor &c)
385 {
386     if (c == m_fillColor)
387         return;
388
389     m_fillColor = c;
390     markDirty(DirtyMaterial);
391 }
392
393 void QSGPainterNode::setContentsScale(qreal s)
394 {
395     if (s == m_contentsScale)
396         return;
397
398     m_contentsScale = s;
399     markDirty(DirtyMaterial);
400 }
401
402
403 QT_END_NAMESPACE