Fix test fails related to QTBUG-22237
[profile/ivi/qtdeclarative.git] / src / declarative / items / qquickshadereffectsource.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 "qquickshadereffectsource_p.h"
43
44 #include "qquickitem_p.h"
45 #include "qquickcanvas_p.h"
46 #include <private/qsgadaptationlayer_p.h>
47 #include <private/qsgrenderer_p.h>
48
49 #include "qopenglframebufferobject.h"
50 #include "qmath.h"
51 #include <private/qsgtexture_p.h>
52
53 QT_BEGIN_NAMESPACE
54
55 DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY)
56
57 class QQuickShaderEffectSourceTextureProvider : public QSGTextureProvider
58 {
59     Q_OBJECT
60 public:
61     QQuickShaderEffectSourceTextureProvider()
62         : sourceTexture(0)
63     {
64     }
65
66     QSGTexture *texture() const {
67         sourceTexture->setMipmapFiltering(mipmapFiltering);
68         sourceTexture->setFiltering(filtering);
69         sourceTexture->setHorizontalWrapMode(horizontalWrap);
70         sourceTexture->setVerticalWrapMode(verticalWrap);
71         return sourceTexture;
72     }
73
74     QQuickShaderEffectTexture *sourceTexture;
75
76     QSGTexture::Filtering mipmapFiltering;
77     QSGTexture::Filtering filtering;
78     QSGTexture::WrapMode horizontalWrap;
79     QSGTexture::WrapMode verticalWrap;
80 };
81 #include "qquickshadereffectsource.moc"
82
83
84 QQuickShaderEffectSourceNode::QQuickShaderEffectSourceNode()
85 {
86     setFlag(UsePreprocess, true);
87 }
88
89 void QQuickShaderEffectSourceNode::markDirtyTexture()
90 {
91     markDirty(DirtyMaterial);
92 }
93
94
95 QQuickShaderEffectTexture::QQuickShaderEffectTexture(QQuickItem *shaderSource)
96     : QSGDynamicTexture()
97     , m_item(0)
98     , m_format(GL_RGBA)
99     , m_shaderSource(shaderSource)
100     , m_renderer(0)
101     , m_fbo(0)
102     , m_secondaryFbo(0)
103 #ifdef QSG_DEBUG_FBO_OVERLAY
104     , m_debugOverlay(0)
105 #endif
106     , m_context(QQuickItemPrivate::get(shaderSource)->sceneGraphContext())
107     , m_mipmap(false)
108     , m_live(true)
109     , m_recursive(false)
110     , m_dirtyTexture(true)
111     , m_multisamplingSupportChecked(false)
112     , m_multisampling(false)
113     , m_grab(false)
114 {
115 }
116
117 QQuickShaderEffectTexture::~QQuickShaderEffectTexture()
118 {
119     delete m_renderer;
120     delete m_fbo;
121     delete m_secondaryFbo;
122 #ifdef QSG_DEBUG_FBO_OVERLAY
123     delete m_debugOverlay;
124 #endif
125 }
126
127 int QQuickShaderEffectTexture::textureId() const
128 {
129     return m_fbo ? m_fbo->texture() : 0;
130 }
131
132 bool QQuickShaderEffectTexture::hasAlphaChannel() const
133 {
134     return m_format != GL_RGB;
135 }
136
137 bool QQuickShaderEffectTexture::hasMipmaps() const
138 {
139     return m_mipmap;
140 }
141
142
143 void QQuickShaderEffectTexture::bind()
144 {
145 #ifndef QT_NO_DEBUG
146     if (!m_recursive && m_fbo && ((m_multisampling && m_secondaryFbo->isBound()) || m_fbo->isBound()))
147         qWarning("ShaderEffectSource: \'recursive\' must be set to true when rendering recursively.");
148 #endif
149     glBindTexture(GL_TEXTURE_2D, m_fbo ? m_fbo->texture() : 0);
150     updateBindOptions();
151 }
152
153 bool QQuickShaderEffectTexture::updateTexture()
154 {
155     if ((m_live || m_grab) && m_dirtyTexture) {
156         grab();
157         m_grab = false;
158         return true;
159     }
160     return false;
161 }
162
163 void QQuickShaderEffectTexture::setHasMipmaps(bool mipmap)
164 {
165     if (mipmap == m_mipmap)
166         return;
167     m_mipmap = mipmap;
168     if (m_mipmap && m_fbo && !m_fbo->format().mipmap())
169         markDirtyTexture();
170 }
171
172
173 void QQuickShaderEffectTexture::setItem(QSGNode *item)
174 {
175     if (item == m_item)
176         return;
177     m_item = item;
178     markDirtyTexture();
179 }
180
181 void QQuickShaderEffectTexture::setRect(const QRectF &rect)
182 {
183     if (rect == m_rect)
184         return;
185     m_rect = rect;
186     markDirtyTexture();
187 }
188
189 void QQuickShaderEffectTexture::setSize(const QSize &size)
190 {
191     if (size == m_size)
192         return;
193     m_size = size;
194     markDirtyTexture();
195 }
196
197 void QQuickShaderEffectTexture::setFormat(GLenum format)
198 {
199     if (format == m_format)
200         return;
201     m_format = format;
202     markDirtyTexture();
203 }
204
205 void QQuickShaderEffectTexture::setLive(bool live)
206 {
207     if (live == m_live)
208         return;
209     m_live = live;
210     markDirtyTexture();
211 }
212
213 void QQuickShaderEffectTexture::scheduleUpdate()
214 {
215     if (m_grab)
216         return;
217     m_grab = true;
218     if (m_dirtyTexture)
219         emit textureChanged();
220 }
221
222 void QQuickShaderEffectTexture::setRecursive(bool recursive)
223 {
224     m_recursive = recursive;
225 }
226
227 void QQuickShaderEffectTexture::markDirtyTexture()
228 {
229     m_dirtyTexture = true;
230     if (m_live || m_grab)
231         emit textureChanged();
232 }
233
234 void QQuickShaderEffectTexture::grab()
235 {
236     if (!m_item || m_size.isNull()) {
237         delete m_fbo;
238         delete m_secondaryFbo;
239         m_fbo = m_secondaryFbo = 0;
240         m_dirtyTexture = false;
241         return;
242     }
243     QSGNode *root = m_item;
244     while (root->firstChild() && root->type() != QSGNode::RootNodeType)
245         root = root->firstChild();
246     if (root->type() != QSGNode::RootNodeType)
247         return;
248
249     if (m_size.isEmpty()) {
250         delete m_fbo;
251         delete m_secondaryFbo;
252         m_secondaryFbo = m_fbo = 0;
253         return;
254     }
255
256     if (!m_renderer) {
257         m_renderer = m_context->createRenderer();
258         connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()), Qt::DirectConnection);
259     }
260     m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
261
262     bool deleteFboLater = false;
263     if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format
264         || (!m_fbo->format().mipmap() && m_mipmap))
265     {
266         if (!m_multisamplingSupportChecked) {
267             QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
268             m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample")
269                             && extensions.contains("GL_EXT_framebuffer_blit");
270             m_multisamplingSupportChecked = true;
271         }
272         if (m_multisampling) {
273             // Don't delete the FBO right away in case it is used recursively.
274             deleteFboLater = true;
275             delete m_secondaryFbo;
276             QOpenGLFramebufferObjectFormat format;
277
278             format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
279             format.setInternalTextureFormat(m_format);
280             format.setSamples(8);
281             m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
282         } else {
283             QOpenGLFramebufferObjectFormat format;
284             format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
285             format.setInternalTextureFormat(m_format);
286             format.setMipmap(m_mipmap);
287             if (m_recursive) {
288                 deleteFboLater = true;
289                 delete m_secondaryFbo;
290                 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
291                 glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
292                 updateBindOptions(true);
293             } else {
294                 delete m_fbo;
295                 delete m_secondaryFbo;
296                 m_fbo = new QOpenGLFramebufferObject(m_size, format);
297                 m_secondaryFbo = 0;
298                 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
299                 updateBindOptions(true);
300             }
301         }
302     }
303
304     if (m_recursive && !m_secondaryFbo) {
305         // m_fbo already created, m_recursive was just set.
306         Q_ASSERT(m_fbo);
307         Q_ASSERT(!m_multisampling);
308
309         m_secondaryFbo = new QOpenGLFramebufferObject(m_size, m_fbo->format());
310         glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
311         updateBindOptions(true);
312     }
313
314     // Render texture.
315     root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
316     m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
317
318 #ifdef QSG_DEBUG_FBO_OVERLAY
319     if (qmlFboOverlay()) {
320         if (!m_debugOverlay)
321             m_debugOverlay = m_context->createRectangleNode();
322         m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height()));
323         m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40));
324         m_debugOverlay->setPenColor(QColor());
325         m_debugOverlay->setPenWidth(0);
326         m_debugOverlay->setRadius(0);
327         m_debugOverlay->update();
328         root->appendChildNode(m_debugOverlay);
329     }
330 #endif
331
332     m_dirtyTexture = false;
333
334     QOpenGLContext *ctx = m_context->glContext();
335     m_renderer->setDeviceRect(m_size);
336     m_renderer->setViewportRect(m_size);
337     QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height());
338     m_renderer->setProjectionMatrixToRect(mirrored);
339     m_renderer->setClearColor(Qt::transparent);
340
341     if (m_multisampling) {
342         m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo));
343
344         if (deleteFboLater) {
345             delete m_fbo;
346             QOpenGLFramebufferObjectFormat format;
347             format.setInternalTextureFormat(m_format);
348             format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
349             format.setMipmap(m_mipmap);
350             format.setSamples(0);
351             m_fbo = new QOpenGLFramebufferObject(m_size, format);
352             glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
353             updateBindOptions(true);
354         }
355
356         QRect r(QPoint(), m_size);
357         QOpenGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r);
358     } else {
359         if (m_recursive) {
360             m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo));
361
362             if (deleteFboLater) {
363                 delete m_fbo;
364                 QOpenGLFramebufferObjectFormat format;
365                 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
366                 format.setInternalTextureFormat(m_format);
367                 format.setMipmap(m_mipmap);
368                 m_fbo = new QOpenGLFramebufferObject(m_size, format);
369                 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
370                 updateBindOptions(true);
371             }
372             qSwap(m_fbo, m_secondaryFbo);
373         } else {
374             m_renderer->renderScene(QSGBindableFbo(m_fbo));
375         }
376     }
377
378     if (m_mipmap) {
379         glBindTexture(GL_TEXTURE_2D, textureId());
380         ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
381     }
382
383     root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
384
385 #ifdef QSG_DEBUG_FBO_OVERLAY
386     if (qmlFboOverlay())
387         root->removeChildNode(m_debugOverlay);
388 #endif
389     if (m_recursive)
390         markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
391 }
392
393 QImage QQuickShaderEffectTexture::toImage() const
394 {
395     if (m_fbo)
396         return m_fbo->toImage();
397
398     return QImage();
399 }
400
401 /*!
402     \qmlclass ShaderEffectSource QQuickShaderEffectSource
403     \since 5.0
404     \ingroup qml-basic-visual-elements
405     \brief The ShaderEffectSource element renders a QML element into a texture
406     and displays it.
407     \inherits Item
408
409     The ShaderEffectSource element renders \l sourceItem into a texture and
410     displays it in the scene. \l sourceItem is drawn into the texture as though
411     it was a fully opaque root element. Thus \l sourceItem itself can be
412     invisible, but still appear in the texture.
413
414     ShaderEffectSource can be used as:
415     \list
416     \o a texture source in a \l ShaderEffect.
417        This allows you to apply custom shader effects to any QML element.
418     \o a cache for a complex element.
419        The complex element can be rendered once into the texture, which can
420        then be animated freely without the need to render the complex element
421        again every frame.
422     \o an opacity layer.
423        ShaderEffectSource allows you to apply an opacity to elements as a group
424        rather than each element individually.
425     \endlist
426
427     \table
428     \row
429     \o \image declarative-shadereffectsource.png
430     \o \qml
431         import QtQuick 2.0
432
433         Rectangle {
434             width: 200
435             height: 100
436             gradient: Gradient {
437                 GradientStop { position: 0; color: "white" }
438                 GradientStop { position: 1; color: "black" }
439             }
440             Row {
441                 opacity: 0.5
442                 Item {
443                     id: foo
444                     width: 100; height: 100
445                     Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
446                     Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
447                     Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
448                 }
449                 ShaderEffectSource {
450                     width: 100; height: 100
451                     sourceItem: foo
452                 }
453             }
454         }
455         \endqml
456     \endrow
457     \endtable
458
459     The ShaderEffectSource element does not redirect any mouse or keyboard
460     input to \l sourceItem. If you hide the \l sourceItem by setting
461     \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero,
462     it will no longer react to input. In cases where the ShaderEffectSource is
463     meant to replace the \l sourceItem, you typically want to hide the
464     \l sourceItem while still handling input. For this, you can use
465     the \l hideSource property.
466
467     \note If \l sourceItem is a \l Rectangle with border, by default half the
468     border width falls outside the texture. To get the whole border, you can
469     extend the \l sourceRect.
470
471     \warning In most cases, using a ShaderEffectSource will decrease
472     performance, and in all cases, it will increase video memory usage.
473     Rendering through a ShaderEffectSource might also lead to lower quality
474     since some OpenGL implementations support multisampled backbuffer,
475     but not multisampled framebuffer objects.
476 */
477
478 QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
479     : QQuickItem(parent)
480     , m_provider(0)
481     , m_texture(0)
482     , m_wrapMode(ClampToEdge)
483     , m_sourceItem(0)
484     , m_textureSize(0, 0)
485     , m_format(RGBA)
486     , m_live(true)
487     , m_hideSource(false)
488     , m_mipmap(false)
489     , m_recursive(false)
490     , m_grab(true)
491 {
492     setFlag(ItemHasContents);
493 }
494
495 QQuickShaderEffectSource::~QQuickShaderEffectSource()
496 {
497     if (m_texture)
498         m_texture->deleteLater();
499
500     if (m_provider)
501         m_provider->deleteLater();
502
503     if (m_sourceItem)
504         QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
505 }
506
507 void QQuickShaderEffectSource::ensureTexture()
508 {
509     if (m_texture)
510         return;
511
512     Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
513                && QQuickItemPrivate::get(this)->sceneGraphContext()
514                && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
515                "QQuickShaderEffectSource::ensureTexture",
516                "Cannot be used outside the rendering thread");
517
518     m_texture = new QQuickShaderEffectTexture(this);
519     connect(m_texture, SIGNAL(textureChanged()), this, SLOT(update()));
520 }
521
522 QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
523 {
524     if (!m_provider) {
525         // Make sure it gets thread affinity on the rendering thread so deletion works properly..
526         Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
527                    && QQuickItemPrivate::get(this)->sceneGraphContext()
528                    && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
529                    "QQuickShaderEffectSource::textureProvider",
530                    "Cannot be used outside the rendering thread");
531         const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
532
533         const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
534         connect(m_texture, SIGNAL(textureChanged()), m_provider, SIGNAL(textureChanged()), Qt::DirectConnection);
535         m_provider->sourceTexture = m_texture;
536     }
537     return m_provider;
538 }
539
540 /*!
541     \qmlproperty enumeration ShaderEffectSource::wrapMode
542
543     This property defines the OpenGL wrap modes associated with the texture.
544     Modifying this property makes most sense when the element is used as a
545     source texture of a \l ShaderEffect.
546
547     \list
548     \o ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
549     \o ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
550     \o ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
551     \o ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
552     \endlist
553
554     \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
555     wrap mode with non-power-of-two textures.
556 */
557
558 QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
559 {
560     return m_wrapMode;
561 }
562
563 void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
564 {
565     if (mode == m_wrapMode)
566         return;
567     m_wrapMode = mode;
568     update();
569     emit wrapModeChanged();
570 }
571
572 /*!
573     \qmlproperty Item ShaderEffectSource::sourceItem
574
575     This property holds the element to be rendered into the texture.
576 */
577
578 QQuickItem *QQuickShaderEffectSource::sourceItem() const
579 {
580     return m_sourceItem;
581 }
582
583 void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
584 {
585     if (item == m_sourceItem)
586         return;
587     if (m_sourceItem)
588         QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
589     m_sourceItem = item;
590     if (m_sourceItem) {
591         // TODO: Find better solution.
592         // 'm_sourceItem' needs a canvas to get a scenegraph node.
593         // The easiest way to make sure it gets a canvas is to
594         // make it a part of the same item tree as 'this'.
595         if (m_sourceItem->parentItem() == 0) {
596             m_sourceItem->setParentItem(this);
597             m_sourceItem->setVisible(false);
598         }
599         QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(m_hideSource);
600     }
601     update();
602     emit sourceItemChanged();
603 }
604
605 /*!
606     \qmlproperty rect ShaderEffectSource::sourceRect
607
608     This property defines which rectangular area of the \l sourceItem to
609     render into the texture. The source rectangle can be larger than
610     \l sourceItem itself. If the rectangle is null, which is the default,
611     the whole \l sourceItem is rendered to texture.
612 */
613
614 QRectF QQuickShaderEffectSource::sourceRect() const
615 {
616     return m_sourceRect;
617 }
618
619 void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
620 {
621     if (rect == m_sourceRect)
622         return;
623     m_sourceRect = rect;
624     update();
625     emit sourceRectChanged();
626 }
627
628 /*!
629     \qmlproperty size ShaderEffectSource::textureSize
630
631     This property holds the requested size of the texture. If it is empty,
632     which is the default, the size of the source rectangle is used.
633
634     \note Some platforms have a limit on how small framebuffer objects can be,
635     which means the actual texture size might be larger than the requested
636     size.
637 */
638
639 QSize QQuickShaderEffectSource::textureSize() const
640 {
641     return m_textureSize;
642 }
643
644 void QQuickShaderEffectSource::setTextureSize(const QSize &size)
645 {
646     if (size == m_textureSize)
647         return;
648     m_textureSize = size;
649     update();
650     emit textureSizeChanged();
651 }
652
653 /*!
654     \qmlproperty enumeration ShaderEffectSource::format
655
656     This property defines the internal OpenGL format of the texture.
657     Modifying this property makes most sense when the element is used as a
658     source texture of a \l ShaderEffect. Depending on the OpenGL
659     implementation, this property might allow you to save some texture memory.
660
661     \list
662     \o ShaderEffectSource.Alpha - GL_ALPHA
663     \o ShaderEffectSource.RGB - GL_RGB
664     \o ShaderEffectSource.RGBA - GL_RGBA
665     \endlist
666
667     \note Some OpenGL implementations do not support the GL_ALPHA format.
668 */
669
670 QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
671 {
672     return m_format;
673 }
674
675 void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
676 {
677     if (format == m_format)
678         return;
679     m_format = format;
680     update();
681     emit formatChanged();
682 }
683
684 /*!
685     \qmlproperty bool ShaderEffectSource::live
686
687     If this property is true, the texture is updated whenever the
688     \l sourceItem changes. Otherwise, it will be a frozen image of the
689     \l sourceItem. The property is true by default.
690 */
691
692 bool QQuickShaderEffectSource::live() const
693 {
694     return m_live;
695 }
696
697 void QQuickShaderEffectSource::setLive(bool live)
698 {
699     if (live == m_live)
700         return;
701     m_live = live;
702     update();
703     emit liveChanged();
704 }
705
706 /*!
707     \qmlproperty bool ShaderEffectSource::hideSource
708
709     If this property is true, the \l sourceItem is hidden, though it will still
710     be rendered into the texture. As opposed to hiding the \l sourceItem by
711     setting \l{Item::visible}{visible} to false, setting this property to true
712     will not prevent mouse or keyboard input from reaching \l sourceItem.
713     The property is useful when the ShaderEffectSource is anchored on top of,
714     and meant to replace the \l sourceItem.
715 */
716
717 bool QQuickShaderEffectSource::hideSource() const
718 {
719     return m_hideSource;
720 }
721
722 void QQuickShaderEffectSource::setHideSource(bool hide)
723 {
724     if (hide == m_hideSource)
725         return;
726     if (m_sourceItem) {
727         QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
728         QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
729     }
730     m_hideSource = hide;
731     update();
732     emit hideSourceChanged();
733 }
734
735 /*!
736     \qmlproperty bool ShaderEffectSource::mipmap
737
738     If this property is true, mipmaps are generated for the texture.
739
740     \note Some OpenGL ES 2 implementations do not support mipmapping of
741     non-power-of-two textures.
742 */
743
744 bool QQuickShaderEffectSource::mipmap() const
745 {
746     return m_mipmap;
747 }
748
749 void QQuickShaderEffectSource::setMipmap(bool enabled)
750 {
751     if (enabled == m_mipmap)
752         return;
753     m_mipmap = enabled;
754     update();
755     emit mipmapChanged();
756 }
757
758 /*!
759     \qmlproperty bool ShaderEffectSource::recursive
760
761     Set this property to true if the ShaderEffectSource has a dependency on
762     itself. ShaderEffectSources form a dependency chain, where one
763     ShaderEffectSource can be part of the \l sourceItem of another.
764     If there is a loop in this chain, a ShaderEffectSource could end up trying
765     to render into the same texture it is using as source, which is not allowed
766     by OpenGL. When this property is set to true, an extra texture is allocated
767     so that ShaderEffectSource can keep a copy of the texture from the previous
768     frame. It can then render into one texture and use the texture from the
769     previous frame as source.
770
771     Setting both this property and \l live to true will cause the scene graph
772     to render continuously. Since the ShaderEffectSource depends on itself,
773     updating it means that it immediately becomes dirty again.
774 */
775
776 bool QQuickShaderEffectSource::recursive() const
777 {
778     return m_recursive;
779 }
780
781 void QQuickShaderEffectSource::setRecursive(bool enabled)
782 {
783     if (enabled == m_recursive)
784         return;
785     m_recursive = enabled;
786     emit recursiveChanged();
787 }
788
789 /*!
790     \qmlmethod ShaderEffectSource::scheduleUpdate()
791
792     Schedules a re-rendering of the texture for the next frame.
793     Use this to update the texture when \l live is false.
794 */
795
796 void QQuickShaderEffectSource::scheduleUpdate()
797 {
798     if (m_grab)
799         return;
800     m_grab = true;
801     update();
802 }
803
804 static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
805 {
806     switch (mode) {
807     case QQuickShaderEffectSource::RepeatHorizontally:
808         *hWrap = QSGTexture::Repeat;
809         *vWrap = QSGTexture::ClampToEdge;
810         break;
811     case QQuickShaderEffectSource::RepeatVertically:
812         *vWrap = QSGTexture::Repeat;
813         *hWrap = QSGTexture::ClampToEdge;
814         break;
815     case QQuickShaderEffectSource::Repeat:
816         *hWrap = *vWrap = QSGTexture::Repeat;
817         break;
818     default:
819         // QQuickShaderEffectSource::ClampToEdge
820         *hWrap = *vWrap = QSGTexture::ClampToEdge;
821         break;
822     }
823 }
824
825
826 QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
827 {
828     if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) {
829         delete oldNode;
830         return 0;
831     }
832
833     ensureTexture();
834
835     QQuickShaderEffectTexture *tex = qobject_cast<QQuickShaderEffectTexture *>(m_texture);
836     tex->setLive(m_live);
837     tex->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
838     QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
839                       ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
840                       : m_sourceRect;
841     tex->setRect(sourceRect);
842     QSize textureSize = m_textureSize.isEmpty()
843                       ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
844                       : m_textureSize;
845     Q_ASSERT(!textureSize.isEmpty());
846     QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(this));
847     const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
848     // Keep power-of-two by doubling the size.
849     while (textureSize.width() < minTextureSize.width())
850         textureSize.rwidth() *= 2;
851     while (textureSize.height() < minTextureSize.height())
852         textureSize.rheight() *= 2;
853
854     tex->setSize(textureSize);
855     tex->setRecursive(m_recursive);
856     tex->setFormat(GLenum(m_format));
857     tex->setHasMipmaps(m_mipmap);
858
859     if (m_grab)
860         tex->scheduleUpdate();
861     m_grab = false;
862
863     QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth
864                                             ? QSGTexture::Linear
865                                             : QSGTexture::Nearest;
866     QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
867     QSGTexture::WrapMode hWrap, vWrap;
868     get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
869
870     if (m_provider) {
871         m_provider->mipmapFiltering = mmFiltering;
872         m_provider->filtering = filtering;
873         m_provider->horizontalWrap = hWrap;
874         m_provider->verticalWrap = vWrap;
875     }
876
877     // Don't create the paint node if we're not spanning any area
878     if (width() == 0 || height() == 0) {
879         delete oldNode;
880         return 0;
881     }
882
883     QQuickShaderEffectSourceNode *node = static_cast<QQuickShaderEffectSourceNode *>(oldNode);
884     if (!node) {
885         node = new QQuickShaderEffectSourceNode;
886         node->setTexture(m_texture);
887         connect(m_texture, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
888     }
889
890     // If live and recursive, update continuously.
891     if (m_live && m_recursive)
892         node->markDirty(QSGNode::DirtyMaterial);
893
894     node->setMipmapFiltering(mmFiltering);
895     node->setFiltering(filtering);
896     node->setHorizontalWrapMode(hWrap);
897     node->setVerticalWrapMode(vWrap);
898     node->setTargetRect(QRectF(0, 0, width(), height()));
899     node->setSourceRect(QRectF(0, 0, 1, 1));
900     node->update();
901
902     return node;
903 }
904
905 QT_END_NAMESPACE