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