Fixed QQuickPaintedItem texture binding when using Image render target.
[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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgpainternode_p.h"
43
44 #include <private/qquickpainteditem_p.h>
45
46 #include <private/qsgcontext_p.h>
47 #include <private/qopenglextensions_p.h>
48 #include <qopenglframebufferobject.h>
49 #include <qopenglfunctions.h>
50 #include <qopenglpaintdevice.h>
51 #include <qmath.h>
52 #include <qpainter.h>
53
54 QT_BEGIN_NAMESPACE
55
56 #define QT_MINIMUM_DYNAMIC_FBO_SIZE 64
57
58 static inline int qt_next_power_of_two(int v)
59 {
60     v--;
61     v |= v >> 1;
62     v |= v >> 2;
63     v |= v >> 4;
64     v |= v >> 8;
65     v |= v >> 16;
66     ++v;
67     return v;
68 }
69
70 QSGPainterTexture::QSGPainterTexture()
71     : QSGPlainTexture()
72 {
73
74 }
75
76 void QSGPainterTexture::bind()
77 {
78     if (m_dirty_rect.isNull()) {
79         QSGPlainTexture::bind();
80         return;
81     }
82
83     bool oldMipmapsGenerated = m_mipmaps_generated;
84     m_mipmaps_generated = true;
85     QSGPlainTexture::bind();
86     m_mipmaps_generated = oldMipmapsGenerated;
87
88     QImage subImage = m_image.copy(m_dirty_rect);
89
90     int w = m_dirty_rect.width();
91     int h = m_dirty_rect.height();
92
93 #ifdef QT_OPENGL_ES
94     glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h,
95                     GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits());
96 #else
97     glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h,
98                     GL_BGRA, GL_UNSIGNED_BYTE, subImage.constBits());
99 #endif
100
101     if (m_has_mipmaps && !m_mipmaps_generated) {
102         QOpenGLContext *ctx = QOpenGLContext::currentContext();
103         ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
104         m_mipmaps_generated = true;
105     }
106
107     m_dirty_texture = false;
108     m_dirty_bind_options = false;
109
110     m_dirty_rect = QRect();
111 }
112
113 QSGPainterNode::QSGPainterNode(QQuickPaintedItem *item)
114     : QSGGeometryNode()
115     , m_preferredRenderTarget(QQuickPaintedItem::Image)
116     , m_actualRenderTarget(QQuickPaintedItem::Image)
117     , m_item(item)
118     , m_fbo(0)
119     , m_multisampledFbo(0)
120     , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
121     , m_texture(0)
122     , m_gl_device(0)
123     , m_size(1, 1)
124     , m_dirtyContents(false)
125     , m_opaquePainting(false)
126     , m_linear_filtering(false)
127     , m_mipmapping(false)
128     , m_smoothPainting(false)
129     , m_extensionsChecked(false)
130     , m_multisamplingSupported(false)
131     , m_fastFBOResizing(false)
132     , m_fillColor(Qt::transparent)
133     , m_contentsScale(1.0)
134     , m_dirtyGeometry(false)
135     , m_dirtyRenderTarget(false)
136     , m_dirtyTexture(false)
137 {
138     m_context = static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphContext();
139
140     setMaterial(&m_materialO);
141     setOpaqueMaterial(&m_material);
142     setGeometry(&m_geometry);
143 }
144
145 QSGPainterNode::~QSGPainterNode()
146 {
147     delete m_texture;
148     delete m_fbo;
149     delete m_multisampledFbo;
150     delete m_gl_device;
151 }
152
153 void QSGPainterNode::paint()
154 {
155     QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect;
156
157     QPainter painter;
158     if (m_actualRenderTarget == QQuickPaintedItem::Image)
159         painter.begin(&m_image);
160     else {
161         if (!m_gl_device) {
162             m_gl_device = new QOpenGLPaintDevice(m_fboSize);
163             m_gl_device->setPaintFlipped(true);
164         }
165
166         if (m_multisampledFbo)
167             m_multisampledFbo->bind();
168         else
169             m_fbo->bind();
170
171         painter.begin(m_gl_device);
172     }
173
174     if (m_smoothPainting) {
175         painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing
176                                | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
177     }
178
179     painter.scale(m_contentsScale, m_contentsScale);
180
181     QRect sclip(qFloor(dirtyRect.x()/m_contentsScale),
182                 qFloor(dirtyRect.y()/m_contentsScale),
183                 qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)),
184                 qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale)));
185
186     if (!m_dirtyRect.isNull())
187         painter.setClipRect(sclip);
188
189     painter.setCompositionMode(QPainter::CompositionMode_Source);
190     painter.fillRect(sclip, m_fillColor);
191     painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
192
193     m_item->paint(&painter);
194     painter.end();
195
196     if (m_actualRenderTarget == QQuickPaintedItem::Image) {
197         m_texture->setImage(m_image);
198         m_texture->setDirtyRect(dirtyRect);
199     } else if (m_multisampledFbo) {
200         QOpenGLFramebufferObject::blitFramebuffer(m_fbo, dirtyRect, m_multisampledFbo, dirtyRect);
201     }
202
203     if (m_multisampledFbo)
204         m_multisampledFbo->release();
205     else if (m_fbo)
206         m_fbo->release();
207
208     m_dirtyRect = QRect();
209 }
210
211 void QSGPainterNode::update()
212 {
213     if (m_dirtyRenderTarget)
214         updateRenderTarget();
215     if (m_dirtyGeometry)
216         updateGeometry();
217     if (m_dirtyTexture)
218         updateTexture();
219
220     if (m_dirtyContents)
221         paint();
222
223     m_dirtyGeometry = false;
224     m_dirtyRenderTarget = false;
225     m_dirtyTexture = false;
226     m_dirtyContents = false;
227 }
228
229 void QSGPainterNode::updateTexture()
230 {
231     m_texture->setHasMipmaps(m_mipmapping);
232     m_texture->setHasAlphaChannel(!m_opaquePainting);
233     m_material.setTexture(m_texture);
234     m_materialO.setTexture(m_texture);
235
236     markDirty(DirtyMaterial);
237 }
238
239 void QSGPainterNode::updateGeometry()
240 {
241     QRectF source;
242     if (m_actualRenderTarget == QQuickPaintedItem::Image)
243         source = QRectF(0, 0, 1, 1);
244     else
245         source = QRectF(0, 0, qreal(m_size.width()) / m_fboSize.width(), qreal(m_size.height()) / m_fboSize.height());
246     QRectF dest(0, 0, m_size.width(), m_size.height());
247     if (m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
248         dest = QRectF(QPointF(0, m_size.height()), QPointF(m_size.width(), 0));
249     QSGGeometry::updateTexturedRectGeometry(&m_geometry,
250                                             dest,
251                                             source);
252     markDirty(DirtyGeometry);
253 }
254
255 void QSGPainterNode::updateRenderTarget()
256 {
257     if (!m_extensionsChecked) {
258         QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
259         m_multisamplingSupported = extensions.contains("GL_EXT_framebuffer_multisample")
260                 && extensions.contains("GL_EXT_framebuffer_blit");
261         m_extensionsChecked = true;
262     }
263
264     m_dirtyContents = true;
265
266     QQuickPaintedItem::RenderTarget oldTarget = m_actualRenderTarget;
267     if (m_preferredRenderTarget == QQuickPaintedItem::Image) {
268         m_actualRenderTarget = QQuickPaintedItem::Image;
269     } else {
270         if (!m_multisamplingSupported && m_smoothPainting)
271             m_actualRenderTarget = QQuickPaintedItem::Image;
272         else
273             m_actualRenderTarget = m_preferredRenderTarget;
274     }
275     if (oldTarget != m_actualRenderTarget) {
276         m_image = QImage();
277         delete m_fbo;
278         delete m_multisampledFbo;
279         m_fbo = m_multisampledFbo = 0;
280     }
281
282     if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject ||
283             m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject) {
284         const QOpenGLContext *ctx = m_context->glContext();
285         if (m_fbo && !m_dirtyGeometry && (!ctx->format().samples() || !m_multisamplingSupported))
286             return;
287
288         if (m_fboSize.isEmpty())
289             updateFBOSize();
290
291         delete m_fbo;
292         delete m_multisampledFbo;
293         m_fbo = m_multisampledFbo = 0;
294
295         if (m_smoothPainting && ctx->format().samples() && m_multisamplingSupported) {
296             {
297                 QOpenGLFramebufferObjectFormat format;
298                 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
299                 format.setSamples(8);
300                 m_multisampledFbo = new QOpenGLFramebufferObject(m_fboSize, format);
301             }
302             {
303                 QOpenGLFramebufferObjectFormat format;
304                 format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
305                 m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
306             }
307         } else {
308             QOpenGLFramebufferObjectFormat format;
309             format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
310             m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
311         }
312     } else {
313         if (!m_image.isNull() && !m_dirtyGeometry)
314             return;
315
316         m_image = QImage(m_size, QImage::Format_ARGB32_Premultiplied);
317         m_image.fill(Qt::transparent);
318     }
319
320     QSGPainterTexture *texture = new QSGPainterTexture;
321     if (m_actualRenderTarget == QQuickPaintedItem::Image) {
322         texture->setOwnsTexture(true);
323         texture->setTextureSize(m_size);
324     } else {
325         texture->setTextureId(m_fbo->texture());
326         texture->setOwnsTexture(false);
327         texture->setTextureSize(m_fboSize);
328     }
329
330     if (m_texture)
331         delete m_texture;
332
333     texture->setTextureSize(m_size);
334     m_texture = texture;
335 }
336
337 void QSGPainterNode::updateFBOSize()
338 {
339     int fboWidth;
340     int fboHeight;
341     if (m_fastFBOResizing) {
342         fboWidth = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.width()));
343         fboHeight = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.height()));
344     } else {
345         QSize minimumFBOSize = m_context->minimumFBOSize();
346         fboWidth = qMax(minimumFBOSize.width(), m_size.width());
347         fboHeight = qMax(minimumFBOSize.height(), m_size.height());
348     }
349
350     m_fboSize = QSize(fboWidth, fboHeight);
351 }
352
353 void QSGPainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target)
354 {
355     if (m_preferredRenderTarget == target)
356         return;
357
358     m_preferredRenderTarget = target;
359
360     m_dirtyRenderTarget = true;
361     m_dirtyGeometry = true;
362     m_dirtyTexture = true;
363 }
364
365 void QSGPainterNode::setSize(const QSize &size)
366 {
367     if (size == m_size)
368         return;
369
370     m_size = size;
371     updateFBOSize();
372
373     if (m_fbo)
374         m_dirtyRenderTarget = m_fbo->size() != m_fboSize || m_dirtyRenderTarget;
375     else
376         m_dirtyRenderTarget = true;
377     m_dirtyGeometry = true;
378     m_dirtyTexture = true;
379 }
380
381 void QSGPainterNode::setDirty(bool d, const QRect &dirtyRect)
382 {
383     m_dirtyContents = d;
384     m_dirtyRect = dirtyRect;
385
386     if (m_mipmapping)
387         m_dirtyTexture = true;
388
389     markDirty(DirtyMaterial);
390 }
391
392 void QSGPainterNode::setOpaquePainting(bool opaque)
393 {
394     if (opaque == m_opaquePainting)
395         return;
396
397     m_opaquePainting = opaque;
398     m_dirtyTexture = true;
399 }
400
401 void QSGPainterNode::setLinearFiltering(bool linearFiltering)
402 {
403     if (linearFiltering == m_linear_filtering)
404         return;
405
406     m_linear_filtering = linearFiltering;
407
408     m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
409     m_materialO.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
410     markDirty(DirtyMaterial);
411 }
412
413 void QSGPainterNode::setMipmapping(bool mipmapping)
414 {
415     if (mipmapping == m_mipmapping)
416         return;
417
418     m_mipmapping = mipmapping;
419     m_material.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
420     m_materialO.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
421     m_dirtyTexture = true;
422 }
423
424 void QSGPainterNode::setSmoothPainting(bool s)
425 {
426     if (s == m_smoothPainting)
427         return;
428
429     m_smoothPainting = s;
430     m_dirtyRenderTarget = true;
431 }
432
433 void QSGPainterNode::setFillColor(const QColor &c)
434 {
435     if (c == m_fillColor)
436         return;
437
438     m_fillColor = c;
439     markDirty(DirtyMaterial);
440 }
441
442 void QSGPainterNode::setContentsScale(qreal s)
443 {
444     if (s == m_contentsScale)
445         return;
446
447     m_contentsScale = s;
448     markDirty(DirtyMaterial);
449 }
450
451 void QSGPainterNode::setFastFBOResizing(bool dynamic)
452 {
453     m_fastFBOResizing = dynamic;
454 }
455
456 QImage QSGPainterNode::toImage() const
457 {
458     if (m_actualRenderTarget == QQuickPaintedItem::Image)
459         return m_image;
460     else
461         return m_fbo->toImage();
462 }
463
464 QT_END_NAMESPACE