1f44750f98063bbc5c0c899a2d67430bb51a6459
[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() || m_texture_id == 0) {
79         QSGPlainTexture::bind();
80     } else {
81         glBindTexture(GL_TEXTURE_2D, m_texture_id);
82
83         QImage subImage = m_image.copy(m_dirty_rect);
84
85         int w = m_dirty_rect.width();
86         int h = m_dirty_rect.height();
87
88 #ifdef QT_OPENGL_ES
89         glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h,
90                         GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits());
91 #else
92         glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h,
93                         GL_BGRA, GL_UNSIGNED_BYTE, subImage.constBits());
94 #endif
95
96         if (m_has_mipmaps && !m_mipmaps_generated) {
97             QOpenGLContext *ctx = QOpenGLContext::currentContext();
98             ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
99             m_mipmaps_generated = true;
100         }
101
102         m_dirty_texture = false;
103         m_dirty_bind_options = false;
104     }
105     m_dirty_rect = QRect();
106 }
107
108 QSGPainterNode::QSGPainterNode(QQuickPaintedItem *item)
109     : QSGGeometryNode()
110     , m_preferredRenderTarget(QQuickPaintedItem::Image)
111     , m_actualRenderTarget(QQuickPaintedItem::Image)
112     , m_item(item)
113     , m_fbo(0)
114     , m_multisampledFbo(0)
115     , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
116     , m_texture(0)
117     , m_gl_device(0)
118     , m_size(1, 1)
119     , m_dirtyContents(false)
120     , m_opaquePainting(false)
121     , m_linear_filtering(false)
122     , m_mipmapping(false)
123     , m_smoothPainting(false)
124     , m_extensionsChecked(false)
125     , m_multisamplingSupported(false)
126     , m_fastFBOResizing(false)
127     , m_fillColor(Qt::transparent)
128     , m_contentsScale(1.0)
129     , m_dirtyGeometry(false)
130     , m_dirtyRenderTarget(false)
131     , m_dirtyTexture(false)
132 {
133     m_context = static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphContext();
134
135     setMaterial(&m_materialO);
136     setOpaqueMaterial(&m_material);
137     setGeometry(&m_geometry);
138 }
139
140 QSGPainterNode::~QSGPainterNode()
141 {
142     delete m_texture;
143     delete m_fbo;
144     delete m_multisampledFbo;
145     delete m_gl_device;
146 }
147
148 void QSGPainterNode::paint()
149 {
150     QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect;
151
152     QPainter painter;
153     if (m_actualRenderTarget == QQuickPaintedItem::Image)
154         painter.begin(&m_image);
155     else {
156         if (!m_gl_device) {
157             m_gl_device = new QOpenGLPaintDevice(m_fboSize);
158             m_gl_device->setPaintFlipped(true);
159         }
160
161         if (m_multisampledFbo)
162             m_multisampledFbo->bind();
163         else
164             m_fbo->bind();
165
166         painter.begin(m_gl_device);
167     }
168
169     if (m_smoothPainting) {
170         painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing
171                                | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
172     }
173
174     painter.scale(m_contentsScale, m_contentsScale);
175
176     QRect sclip(qFloor(dirtyRect.x()/m_contentsScale),
177                 qFloor(dirtyRect.y()/m_contentsScale),
178                 qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)),
179                 qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale)));
180
181     if (!m_dirtyRect.isNull())
182         painter.setClipRect(sclip);
183
184     painter.setCompositionMode(QPainter::CompositionMode_Source);
185     painter.fillRect(sclip, m_fillColor);
186     painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
187
188     m_item->paint(&painter);
189     painter.end();
190
191     if (m_actualRenderTarget == QQuickPaintedItem::Image) {
192         m_texture->setImage(m_image);
193         m_texture->setDirtyRect(dirtyRect);
194     } else if (m_multisampledFbo) {
195         QOpenGLFramebufferObject::blitFramebuffer(m_fbo, dirtyRect, m_multisampledFbo, dirtyRect);
196     }
197
198     if (m_multisampledFbo)
199         m_multisampledFbo->release();
200     else if (m_fbo)
201         m_fbo->release();
202
203     m_dirtyRect = QRect();
204 }
205
206 void QSGPainterNode::update()
207 {
208     if (m_dirtyRenderTarget)
209         updateRenderTarget();
210     if (m_dirtyGeometry)
211         updateGeometry();
212     if (m_dirtyTexture)
213         updateTexture();
214
215     if (m_dirtyContents)
216         paint();
217
218     m_dirtyGeometry = false;
219     m_dirtyRenderTarget = false;
220     m_dirtyTexture = false;
221     m_dirtyContents = false;
222 }
223
224 void QSGPainterNode::updateTexture()
225 {
226     m_texture->setHasMipmaps(m_mipmapping);
227     m_texture->setHasAlphaChannel(!m_opaquePainting);
228     m_material.setTexture(m_texture);
229     m_materialO.setTexture(m_texture);
230
231     markDirty(DirtyMaterial);
232 }
233
234 void QSGPainterNode::updateGeometry()
235 {
236     QRectF source;
237     if (m_actualRenderTarget == QQuickPaintedItem::Image)
238         source = QRectF(0, 0, 1, 1);
239     else
240         source = QRectF(0, 0, qreal(m_size.width()) / m_fboSize.width(), qreal(m_size.height()) / m_fboSize.height());
241     QRectF dest(0, 0, m_size.width(), m_size.height());
242     if (m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
243         dest = QRectF(QPointF(0, m_size.height()), QPointF(m_size.width(), 0));
244     QSGGeometry::updateTexturedRectGeometry(&m_geometry,
245                                             dest,
246                                             source);
247     markDirty(DirtyGeometry);
248 }
249
250 void QSGPainterNode::updateRenderTarget()
251 {
252     if (!m_extensionsChecked) {
253         QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
254         m_multisamplingSupported = extensions.contains("GL_EXT_framebuffer_multisample")
255                 && extensions.contains("GL_EXT_framebuffer_blit");
256         m_extensionsChecked = true;
257     }
258
259     m_dirtyContents = true;
260
261     QQuickPaintedItem::RenderTarget oldTarget = m_actualRenderTarget;
262     if (m_preferredRenderTarget == QQuickPaintedItem::Image) {
263         m_actualRenderTarget = QQuickPaintedItem::Image;
264     } else {
265         if (!m_multisamplingSupported && m_smoothPainting)
266             m_actualRenderTarget = QQuickPaintedItem::Image;
267         else
268             m_actualRenderTarget = m_preferredRenderTarget;
269     }
270     if (oldTarget != m_actualRenderTarget) {
271         m_image = QImage();
272         delete m_fbo;
273         delete m_multisampledFbo;
274         m_fbo = m_multisampledFbo = 0;
275     }
276
277     if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject ||
278             m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject) {
279         const QOpenGLContext *ctx = m_context->glContext();
280         if (m_fbo && !m_dirtyGeometry && (!ctx->format().samples() || !m_multisamplingSupported))
281             return;
282
283         if (m_fboSize.isEmpty())
284             updateFBOSize();
285
286         delete m_fbo;
287         delete m_multisampledFbo;
288         m_fbo = m_multisampledFbo = 0;
289
290         if (m_smoothPainting && ctx->format().samples() && m_multisamplingSupported) {
291             {
292                 QOpenGLFramebufferObjectFormat format;
293                 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
294                 format.setSamples(8);
295                 m_multisampledFbo = new QOpenGLFramebufferObject(m_fboSize, format);
296             }
297             {
298                 QOpenGLFramebufferObjectFormat format;
299                 format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
300                 m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
301             }
302         } else {
303             QOpenGLFramebufferObjectFormat format;
304             format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
305             m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
306         }
307     } else {
308         if (!m_image.isNull() && !m_dirtyGeometry)
309             return;
310
311         m_image = QImage(m_size, QImage::Format_ARGB32_Premultiplied);
312         m_image.fill(Qt::transparent);
313     }
314
315     QSGPainterTexture *texture = new QSGPainterTexture;
316     if (m_actualRenderTarget == QQuickPaintedItem::Image) {
317         texture->setOwnsTexture(true);
318         texture->setTextureSize(m_size);
319     } else {
320         texture->setTextureId(m_fbo->texture());
321         texture->setOwnsTexture(false);
322         texture->setTextureSize(m_fboSize);
323     }
324
325     if (m_texture)
326         delete m_texture;
327
328     texture->setTextureSize(m_size);
329     m_texture = texture;
330 }
331
332 void QSGPainterNode::updateFBOSize()
333 {
334     int fboWidth;
335     int fboHeight;
336     if (m_fastFBOResizing) {
337         fboWidth = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.width()));
338         fboHeight = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.height()));
339     } else {
340         QSize minimumFBOSize = m_context->minimumFBOSize();
341         fboWidth = qMax(minimumFBOSize.width(), m_size.width());
342         fboHeight = qMax(minimumFBOSize.height(), m_size.height());
343     }
344
345     m_fboSize = QSize(fboWidth, fboHeight);
346 }
347
348 void QSGPainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target)
349 {
350     if (m_preferredRenderTarget == target)
351         return;
352
353     m_preferredRenderTarget = target;
354
355     m_dirtyRenderTarget = true;
356     m_dirtyGeometry = true;
357     m_dirtyTexture = true;
358 }
359
360 void QSGPainterNode::setSize(const QSize &size)
361 {
362     if (size == m_size)
363         return;
364
365     m_size = size;
366     updateFBOSize();
367
368     if (m_fbo)
369         m_dirtyRenderTarget = m_fbo->size() != m_fboSize || m_dirtyRenderTarget;
370     else
371         m_dirtyRenderTarget = true;
372     m_dirtyGeometry = true;
373     m_dirtyTexture = true;
374 }
375
376 void QSGPainterNode::setDirty(bool d, const QRect &dirtyRect)
377 {
378     m_dirtyContents = d;
379     m_dirtyRect = dirtyRect;
380
381     if (m_mipmapping)
382         m_dirtyTexture = true;
383
384     markDirty(DirtyMaterial);
385 }
386
387 void QSGPainterNode::setOpaquePainting(bool opaque)
388 {
389     if (opaque == m_opaquePainting)
390         return;
391
392     m_opaquePainting = opaque;
393     m_dirtyTexture = true;
394 }
395
396 void QSGPainterNode::setLinearFiltering(bool linearFiltering)
397 {
398     if (linearFiltering == m_linear_filtering)
399         return;
400
401     m_linear_filtering = linearFiltering;
402
403     m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
404     m_materialO.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
405     markDirty(DirtyMaterial);
406 }
407
408 void QSGPainterNode::setMipmapping(bool mipmapping)
409 {
410     if (mipmapping == m_mipmapping)
411         return;
412
413     m_mipmapping = mipmapping;
414     m_material.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
415     m_materialO.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
416     m_dirtyTexture = true;
417 }
418
419 void QSGPainterNode::setSmoothPainting(bool s)
420 {
421     if (s == m_smoothPainting)
422         return;
423
424     m_smoothPainting = s;
425     m_dirtyRenderTarget = true;
426 }
427
428 void QSGPainterNode::setFillColor(const QColor &c)
429 {
430     if (c == m_fillColor)
431         return;
432
433     m_fillColor = c;
434     markDirty(DirtyMaterial);
435 }
436
437 void QSGPainterNode::setContentsScale(qreal s)
438 {
439     if (s == m_contentsScale)
440         return;
441
442     m_contentsScale = s;
443     markDirty(DirtyMaterial);
444 }
445
446 void QSGPainterNode::setFastFBOResizing(bool dynamic)
447 {
448     m_fastFBOResizing = dynamic;
449 }
450
451 QImage QSGPainterNode::toImage() const
452 {
453     if (m_actualRenderTarget == QQuickPaintedItem::Image)
454         return m_image;
455     else
456         return m_fbo->toImage();
457 }
458
459 QT_END_NAMESPACE