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