876c27e97db3c051209437f4d8051beb0fe41434
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / util / qsgpainternode.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
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 <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 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     if (m_image.isNull())
158         return;
159
160     QPainter painter;
161     if (m_actualRenderTarget == QQuickPaintedItem::Image)
162         painter.begin(&m_image);
163     else {
164         if (!m_gl_device) {
165             m_gl_device = new QOpenGLPaintDevice(m_fboSize);
166             m_gl_device->setPaintFlipped(true);
167         }
168
169         if (m_multisampledFbo)
170             m_multisampledFbo->bind();
171         else
172             m_fbo->bind();
173
174         painter.begin(m_gl_device);
175     }
176
177     if (m_smoothPainting) {
178         painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing
179                                | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
180     }
181
182     painter.scale(m_contentsScale, m_contentsScale);
183
184     QRect sclip(qFloor(dirtyRect.x()/m_contentsScale),
185                 qFloor(dirtyRect.y()/m_contentsScale),
186                 qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)),
187                 qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale)));
188
189     if (!m_dirtyRect.isNull())
190         painter.setClipRect(sclip);
191
192     painter.setCompositionMode(QPainter::CompositionMode_Source);
193     painter.fillRect(sclip, m_fillColor);
194     painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
195
196     m_item->paint(&painter);
197     painter.end();
198
199     if (m_actualRenderTarget == QQuickPaintedItem::Image) {
200         m_texture->setImage(m_image);
201         m_texture->setDirtyRect(dirtyRect);
202     } else if (m_multisampledFbo) {
203         QOpenGLFramebufferObject::blitFramebuffer(m_fbo, dirtyRect, m_multisampledFbo, dirtyRect);
204     }
205
206     if (m_multisampledFbo)
207         m_multisampledFbo->release();
208     else if (m_fbo)
209         m_fbo->release();
210
211     m_dirtyRect = QRect();
212 }
213
214 void QSGPainterNode::update()
215 {
216     if (m_dirtyRenderTarget)
217         updateRenderTarget();
218     if (m_dirtyGeometry)
219         updateGeometry();
220     if (m_dirtyTexture)
221         updateTexture();
222
223     if (m_dirtyContents)
224         paint();
225
226     m_dirtyGeometry = false;
227     m_dirtyRenderTarget = false;
228     m_dirtyTexture = false;
229     m_dirtyContents = false;
230 }
231
232 void QSGPainterNode::updateTexture()
233 {
234     m_texture->setHasMipmaps(m_mipmapping);
235     m_texture->setHasAlphaChannel(!m_opaquePainting);
236     m_material.setTexture(m_texture);
237     m_materialO.setTexture(m_texture);
238
239     markDirty(DirtyMaterial);
240 }
241
242 void QSGPainterNode::updateGeometry()
243 {
244     QRectF source;
245     if (m_actualRenderTarget == QQuickPaintedItem::Image)
246         source = QRectF(0, 0, 1, 1);
247     else
248         source = QRectF(0, 0, qreal(m_size.width()) / m_fboSize.width(), qreal(m_size.height()) / m_fboSize.height());
249     QRectF dest(0, 0, m_size.width(), m_size.height());
250     if (m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
251         dest = QRectF(QPointF(0, m_size.height()), QPointF(m_size.width(), 0));
252     QSGGeometry::updateTexturedRectGeometry(&m_geometry,
253                                             dest,
254                                             source);
255     markDirty(DirtyGeometry);
256 }
257
258 void QSGPainterNode::updateRenderTarget()
259 {
260     if (!m_extensionsChecked) {
261         QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
262         m_multisamplingSupported = extensions.contains("GL_EXT_framebuffer_multisample")
263                 && extensions.contains("GL_EXT_framebuffer_blit");
264         m_extensionsChecked = true;
265     }
266
267     m_dirtyContents = true;
268
269     QQuickPaintedItem::RenderTarget oldTarget = m_actualRenderTarget;
270     if (m_preferredRenderTarget == QQuickPaintedItem::Image) {
271         m_actualRenderTarget = QQuickPaintedItem::Image;
272     } else {
273         if (!m_multisamplingSupported && m_smoothPainting)
274             m_actualRenderTarget = QQuickPaintedItem::Image;
275         else
276             m_actualRenderTarget = m_preferredRenderTarget;
277     }
278     if (oldTarget != m_actualRenderTarget) {
279         m_image = QImage();
280         delete m_fbo;
281         delete m_multisampledFbo;
282         m_fbo = m_multisampledFbo = 0;
283     }
284
285     if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject ||
286             m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject) {
287         const QOpenGLContext *ctx = m_context->glContext();
288         if (m_fbo && !m_dirtyGeometry && (!ctx->format().samples() || !m_multisamplingSupported))
289             return;
290
291         if (m_fboSize.isEmpty())
292             updateFBOSize();
293
294         delete m_fbo;
295         delete m_multisampledFbo;
296         m_fbo = m_multisampledFbo = 0;
297
298         if (m_smoothPainting && ctx->format().samples() && m_multisamplingSupported) {
299             {
300                 QOpenGLFramebufferObjectFormat format;
301                 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
302                 format.setSamples(8);
303                 m_multisampledFbo = new QOpenGLFramebufferObject(m_fboSize, format);
304             }
305             {
306                 QOpenGLFramebufferObjectFormat format;
307                 format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
308                 m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
309             }
310         } else {
311             QOpenGLFramebufferObjectFormat format;
312             format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
313             m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
314         }
315     } else {
316         if (!m_image.isNull() && !m_dirtyGeometry)
317             return;
318
319         m_image = QImage(m_size, QImage::Format_ARGB32_Premultiplied);
320         m_image.fill(Qt::transparent);
321     }
322
323     QSGPainterTexture *texture = new QSGPainterTexture;
324     if (m_actualRenderTarget == QQuickPaintedItem::Image) {
325         texture->setOwnsTexture(true);
326         texture->setTextureSize(m_size);
327     } else {
328         texture->setTextureId(m_fbo->texture());
329         texture->setOwnsTexture(false);
330         texture->setTextureSize(m_fboSize);
331     }
332
333     if (m_texture)
334         delete m_texture;
335
336     texture->setTextureSize(m_size);
337     m_texture = texture;
338 }
339
340 void QSGPainterNode::updateFBOSize()
341 {
342     int fboWidth;
343     int fboHeight;
344     if (m_fastFBOResizing) {
345         fboWidth = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.width()));
346         fboHeight = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.height()));
347     } else {
348         QSize minimumFBOSize = m_context->minimumFBOSize();
349         fboWidth = qMax(minimumFBOSize.width(), m_size.width());
350         fboHeight = qMax(minimumFBOSize.height(), m_size.height());
351     }
352
353     m_fboSize = QSize(fboWidth, fboHeight);
354 }
355
356 void QSGPainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target)
357 {
358     if (m_preferredRenderTarget == target)
359         return;
360
361     m_preferredRenderTarget = target;
362
363     m_dirtyRenderTarget = true;
364     m_dirtyGeometry = true;
365     m_dirtyTexture = true;
366 }
367
368 void QSGPainterNode::setSize(const QSize &size)
369 {
370     if (size == m_size)
371         return;
372
373     m_size = size;
374     updateFBOSize();
375
376     if (m_fbo)
377         m_dirtyRenderTarget = m_fbo->size() != m_fboSize || m_dirtyRenderTarget;
378     else
379         m_dirtyRenderTarget = true;
380     m_dirtyGeometry = true;
381     m_dirtyTexture = true;
382 }
383
384 void QSGPainterNode::setDirty(const QRect &dirtyRect)
385 {
386     m_dirtyContents = true;
387     m_dirtyRect = dirtyRect;
388
389     if (m_mipmapping)
390         m_dirtyTexture = true;
391
392     markDirty(DirtyMaterial);
393 }
394
395 void QSGPainterNode::setOpaquePainting(bool opaque)
396 {
397     if (opaque == m_opaquePainting)
398         return;
399
400     m_opaquePainting = opaque;
401     m_dirtyTexture = true;
402 }
403
404 void QSGPainterNode::setLinearFiltering(bool linearFiltering)
405 {
406     if (linearFiltering == m_linear_filtering)
407         return;
408
409     m_linear_filtering = linearFiltering;
410
411     m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
412     m_materialO.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
413     markDirty(DirtyMaterial);
414 }
415
416 void QSGPainterNode::setMipmapping(bool mipmapping)
417 {
418     if (mipmapping == m_mipmapping)
419         return;
420
421     m_mipmapping = mipmapping;
422     m_material.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
423     m_materialO.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
424     m_dirtyTexture = true;
425 }
426
427 void QSGPainterNode::setSmoothPainting(bool s)
428 {
429     if (s == m_smoothPainting)
430         return;
431
432     m_smoothPainting = s;
433     m_dirtyRenderTarget = true;
434 }
435
436 void QSGPainterNode::setFillColor(const QColor &c)
437 {
438     if (c == m_fillColor)
439         return;
440
441     m_fillColor = c;
442     markDirty(DirtyMaterial);
443 }
444
445 void QSGPainterNode::setContentsScale(qreal s)
446 {
447     if (s == m_contentsScale)
448         return;
449
450     m_contentsScale = s;
451     markDirty(DirtyMaterial);
452 }
453
454 void QSGPainterNode::setFastFBOResizing(bool dynamic)
455 {
456     m_fastFBOResizing = dynamic;
457 }
458
459 QImage QSGPainterNode::toImage() const
460 {
461     if (m_actualRenderTarget == QQuickPaintedItem::Image)
462         return m_image;
463     else
464         return m_fbo->toImage();
465 }
466
467 QT_END_NAMESPACE