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