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