Change antialiasing method for QML2.
[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_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 QML element into a texture and displays it
465
466     The ShaderEffectSource element 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 element. 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 QML element.
475     \li a cache for a complex element.
476        The complex element can be rendered once into the texture, which can
477        then be animated freely without the need to render the complex element
478        again every frame.
479     \li an opacity layer.
480        ShaderEffectSource allows you to apply an opacity to elements as a group
481        rather than each element 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     \endrow
514     \endtable
515
516     The ShaderEffectSource element does not redirect any mouse or keyboard
517     input to \l sourceItem. If you hide the \l sourceItem by setting
518     \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero,
519     it will no longer react to input. In cases where the ShaderEffectSource is
520     meant to replace the \l sourceItem, you typically want to hide the
521     \l sourceItem while still handling input. For this, you can use
522     the \l hideSource property.
523
524     \note If \l sourceItem is a \l Rectangle with border, by default half the
525     border width falls outside the texture. To get the whole border, you can
526     extend the \l sourceRect.
527
528     \note The ShaderEffectSource relies on FBO multisampling support
529     to antialias edges. If the underlying hardware does not support this,
530     which is the case for most embedded graphics chips, edges rendered
531     inside a ShaderEffectSource will not be antialiased. One way to remedy
532     this is to double the size of the effect source and render it with
533     \c {smooth: true}. This will be equivalent to 4x multisampling, at
534     the cost of lower performance and higher memory use.
535
536     \warning In most cases, using a ShaderEffectSource will decrease
537     performance, and in all cases, it will increase video memory usage.
538     Rendering through a ShaderEffectSource might also lead to lower quality
539     since some OpenGL implementations support multisampled backbuffer,
540     but not multisampled framebuffer objects.
541 */
542
543 QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
544     : QQuickItem(parent)
545     , m_provider(0)
546     , m_texture(0)
547     , m_wrapMode(ClampToEdge)
548     , m_sourceItem(0)
549     , m_textureSize(0, 0)
550     , m_format(RGBA)
551     , m_live(true)
552     , m_hideSource(false)
553     , m_mipmap(false)
554     , m_recursive(false)
555     , m_grab(true)
556 {
557     setFlag(ItemHasContents);
558 }
559
560 QQuickShaderEffectSource::~QQuickShaderEffectSource()
561 {
562     if (m_texture)
563         m_texture->deleteLater();
564
565     if (m_provider)
566         m_provider->deleteLater();
567
568     if (m_sourceItem) {
569         QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem);
570         sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
571         sd->derefFromEffectItem(m_hideSource);
572         if (canvas())
573             sd->derefCanvas();
574     }
575 }
576
577 void QQuickShaderEffectSource::ensureTexture()
578 {
579     if (m_texture)
580         return;
581
582     Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
583                && QQuickItemPrivate::get(this)->sceneGraphContext()
584                && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
585                "QQuickShaderEffectSource::ensureTexture",
586                "Cannot be used outside the rendering thread");
587
588     m_texture = new QQuickShaderEffectTexture(this);
589     connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update()));
590     connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted()));
591 }
592
593 QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
594 {
595     if (!m_provider) {
596         // Make sure it gets thread affinity on the rendering thread so deletion works properly..
597         Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
598                    && QQuickItemPrivate::get(this)->sceneGraphContext()
599                    && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
600                    "QQuickShaderEffectSource::textureProvider",
601                    "Cannot be used outside the rendering thread");
602         const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
603         const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
604         connect(m_texture, SIGNAL(updateRequested()), m_provider, SIGNAL(textureChanged()));
605         m_provider->sourceTexture = m_texture;
606     }
607     return m_provider;
608 }
609
610 /*!
611     \qmlproperty enumeration ShaderEffectSource::wrapMode
612
613     This property defines the OpenGL wrap modes associated with the texture.
614     Modifying this property makes most sense when the element is used as a
615     source texture of a \l ShaderEffect.
616
617     \list
618     \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
619     \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
620     \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
621     \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
622     \endlist
623
624     \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
625     wrap mode with non-power-of-two textures.
626 */
627
628 QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
629 {
630     return m_wrapMode;
631 }
632
633 void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
634 {
635     if (mode == m_wrapMode)
636         return;
637     m_wrapMode = mode;
638     update();
639     emit wrapModeChanged();
640 }
641
642 /*!
643     \qmlproperty Item ShaderEffectSource::sourceItem
644
645     This property holds the element to be rendered into the texture.
646     Setting this to null while \l live is true, will release the texture
647     resources.
648 */
649
650 QQuickItem *QQuickShaderEffectSource::sourceItem() const
651 {
652     return m_sourceItem;
653 }
654
655 void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect)
656 {
657     Q_ASSERT(item == m_sourceItem);
658     Q_UNUSED(item);
659     if (newRect.size() != oldRect.size())
660         update();
661 }
662
663 void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
664 {
665     if (item == m_sourceItem)
666         return;
667     if (m_sourceItem) {
668         QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem);
669         d->derefFromEffectItem(m_hideSource);
670         d->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
671         disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
672         if (canvas())
673             d->derefCanvas();
674     }
675     m_sourceItem = item;
676
677     if (item) {
678         QQuickItemPrivate *d = QQuickItemPrivate::get(item);
679         // 'item' needs a canvas to get a scene graph node. It usually gets one through its
680         // parent, but if the source item is "inline" rather than a reference -- i.e.
681         // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent.
682         // In those cases, 'item' should get the canvas from 'this'.
683         if (canvas())
684             d->refCanvas(canvas());
685         d->refFromEffectItem(m_hideSource);
686         d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
687         connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
688     }
689     update();
690     emit sourceItemChanged();
691 }
692
693 void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
694 {
695     Q_ASSERT(item == m_sourceItem);
696     m_sourceItem = 0;
697     update();
698     emit sourceItemChanged();
699 }
700
701
702 /*!
703     \qmlproperty rect ShaderEffectSource::sourceRect
704
705     This property defines which rectangular area of the \l sourceItem to
706     render into the texture. The source rectangle can be larger than
707     \l sourceItem itself. If the rectangle is null, which is the default,
708     the whole \l sourceItem is rendered to texture.
709 */
710
711 QRectF QQuickShaderEffectSource::sourceRect() const
712 {
713     return m_sourceRect;
714 }
715
716 void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
717 {
718     if (rect == m_sourceRect)
719         return;
720     m_sourceRect = rect;
721     update();
722     emit sourceRectChanged();
723 }
724
725 /*!
726     \qmlproperty size ShaderEffectSource::textureSize
727
728     This property holds the requested size of the texture. If it is empty,
729     which is the default, the size of the source rectangle is used.
730
731     \note Some platforms have a limit on how small framebuffer objects can be,
732     which means the actual texture size might be larger than the requested
733     size.
734 */
735
736 QSize QQuickShaderEffectSource::textureSize() const
737 {
738     return m_textureSize;
739 }
740
741 void QQuickShaderEffectSource::setTextureSize(const QSize &size)
742 {
743     if (size == m_textureSize)
744         return;
745     m_textureSize = size;
746     update();
747     emit textureSizeChanged();
748 }
749
750 /*!
751     \qmlproperty enumeration ShaderEffectSource::format
752
753     This property defines the internal OpenGL format of the texture.
754     Modifying this property makes most sense when the element is used as a
755     source texture of a \l ShaderEffect. Depending on the OpenGL
756     implementation, this property might allow you to save some texture memory.
757
758     \list
759     \li ShaderEffectSource.Alpha - GL_ALPHA
760     \li ShaderEffectSource.RGB - GL_RGB
761     \li ShaderEffectSource.RGBA - GL_RGBA
762     \endlist
763
764     \note Some OpenGL implementations do not support the GL_ALPHA format.
765 */
766
767 QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
768 {
769     return m_format;
770 }
771
772 void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
773 {
774     if (format == m_format)
775         return;
776     m_format = format;
777     update();
778     emit formatChanged();
779 }
780
781 /*!
782     \qmlproperty bool ShaderEffectSource::live
783
784     If this property is true, the texture is updated whenever the
785     \l sourceItem updates. Otherwise, it will be a frozen image, even if
786     \l sourceItem is assigned a new element. The property is true by default.
787 */
788
789 bool QQuickShaderEffectSource::live() const
790 {
791     return m_live;
792 }
793
794 void QQuickShaderEffectSource::setLive(bool live)
795 {
796     if (live == m_live)
797         return;
798     m_live = live;
799     update();
800     emit liveChanged();
801 }
802
803 /*!
804     \qmlproperty bool ShaderEffectSource::hideSource
805
806     If this property is true, the \l sourceItem is hidden, though it will still
807     be rendered into the texture. As opposed to hiding the \l sourceItem by
808     setting \l{Item::visible}{visible} to false, setting this property to true
809     will not prevent mouse or keyboard input from reaching \l sourceItem.
810     The property is useful when the ShaderEffectSource is anchored on top of,
811     and meant to replace the \l sourceItem.
812 */
813
814 bool QQuickShaderEffectSource::hideSource() const
815 {
816     return m_hideSource;
817 }
818
819 void QQuickShaderEffectSource::setHideSource(bool hide)
820 {
821     if (hide == m_hideSource)
822         return;
823     if (m_sourceItem) {
824         QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
825         QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
826     }
827     m_hideSource = hide;
828     update();
829     emit hideSourceChanged();
830 }
831
832 /*!
833     \qmlproperty bool ShaderEffectSource::mipmap
834
835     If this property is true, mipmaps are generated for the texture.
836
837     \note Some OpenGL ES 2 implementations do not support mipmapping of
838     non-power-of-two textures.
839 */
840
841 bool QQuickShaderEffectSource::mipmap() const
842 {
843     return m_mipmap;
844 }
845
846 void QQuickShaderEffectSource::setMipmap(bool enabled)
847 {
848     if (enabled == m_mipmap)
849         return;
850     m_mipmap = enabled;
851     update();
852     emit mipmapChanged();
853 }
854
855 /*!
856     \qmlproperty bool ShaderEffectSource::recursive
857
858     Set this property to true if the ShaderEffectSource has a dependency on
859     itself. ShaderEffectSources form a dependency chain, where one
860     ShaderEffectSource can be part of the \l sourceItem of another.
861     If there is a loop in this chain, a ShaderEffectSource could end up trying
862     to render into the same texture it is using as source, which is not allowed
863     by OpenGL. When this property is set to true, an extra texture is allocated
864     so that ShaderEffectSource can keep a copy of the texture from the previous
865     frame. It can then render into one texture and use the texture from the
866     previous frame as source.
867
868     Setting both this property and \l live to true will cause the scene graph
869     to render continuously. Since the ShaderEffectSource depends on itself,
870     updating it means that it immediately becomes dirty again.
871 */
872
873 bool QQuickShaderEffectSource::recursive() const
874 {
875     return m_recursive;
876 }
877
878 void QQuickShaderEffectSource::setRecursive(bool enabled)
879 {
880     if (enabled == m_recursive)
881         return;
882     m_recursive = enabled;
883     emit recursiveChanged();
884 }
885
886 /*!
887     \qmlmethod ShaderEffectSource::scheduleUpdate()
888
889     Schedules a re-rendering of the texture for the next frame.
890     Use this to update the texture when \l live is false.
891 */
892
893 void QQuickShaderEffectSource::scheduleUpdate()
894 {
895     if (m_grab)
896         return;
897     m_grab = true;
898     update();
899 }
900
901 static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
902 {
903     switch (mode) {
904     case QQuickShaderEffectSource::RepeatHorizontally:
905         *hWrap = QSGTexture::Repeat;
906         *vWrap = QSGTexture::ClampToEdge;
907         break;
908     case QQuickShaderEffectSource::RepeatVertically:
909         *vWrap = QSGTexture::Repeat;
910         *hWrap = QSGTexture::ClampToEdge;
911         break;
912     case QQuickShaderEffectSource::Repeat:
913         *hWrap = *vWrap = QSGTexture::Repeat;
914         break;
915     default:
916         // QQuickShaderEffectSource::ClampToEdge
917         *hWrap = *vWrap = QSGTexture::ClampToEdge;
918         break;
919     }
920 }
921
922
923 void QQuickShaderEffectSource::releaseResources()
924 {
925     if (m_texture) {
926         m_texture->deleteLater();
927         m_texture = 0;
928     }
929     if (m_provider) {
930         m_provider->deleteLater();
931         m_provider = 0;
932     }
933 }
934
935 QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
936 {
937     if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) {
938         if (m_texture)
939             m_texture->setItem(0);
940         delete oldNode;
941         return 0;
942     }
943
944     ensureTexture();
945
946     m_texture->setLive(m_live);
947     m_texture->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
948     QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
949                       ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
950                       : m_sourceRect;
951     m_texture->setRect(sourceRect);
952     QSize textureSize = m_textureSize.isEmpty()
953                       ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
954                       : m_textureSize;
955     Q_ASSERT(!textureSize.isEmpty());
956     QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(this));
957     const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
958     // Keep power-of-two by doubling the size.
959     while (textureSize.width() < minTextureSize.width())
960         textureSize.rwidth() *= 2;
961     while (textureSize.height() < minTextureSize.height())
962         textureSize.rheight() *= 2;
963
964     m_texture->setSize(textureSize);
965     m_texture->setRecursive(m_recursive);
966     m_texture->setFormat(GLenum(m_format));
967     m_texture->setHasMipmaps(m_mipmap);
968
969     if (m_grab)
970         m_texture->scheduleUpdate();
971     m_grab = false;
972
973     QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth
974                                             ? QSGTexture::Linear
975                                             : QSGTexture::Nearest;
976     QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
977     QSGTexture::WrapMode hWrap, vWrap;
978     get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
979
980     if (m_provider) {
981         m_provider->mipmapFiltering = mmFiltering;
982         m_provider->filtering = filtering;
983         m_provider->horizontalWrap = hWrap;
984         m_provider->verticalWrap = vWrap;
985     }
986
987     // Don't create the paint node if we're not spanning any area
988     if (width() == 0 || height() == 0) {
989         delete oldNode;
990         return 0;
991     }
992
993     QQuickShaderEffectSourceNode *node = static_cast<QQuickShaderEffectSourceNode *>(oldNode);
994     if (!node) {
995         node = new QQuickShaderEffectSourceNode;
996         node->setTexture(m_texture);
997         connect(m_texture, SIGNAL(updateRequested()), node, SLOT(markDirtyTexture()));
998     }
999
1000     // If live and recursive, update continuously.
1001     if (m_live && m_recursive)
1002         node->markDirty(QSGNode::DirtyMaterial);
1003
1004     node->setMipmapFiltering(mmFiltering);
1005     node->setFiltering(filtering);
1006     node->setHorizontalWrapMode(hWrap);
1007     node->setVerticalWrapMode(vWrap);
1008     node->setTargetRect(QRectF(0, 0, width(), height()));
1009     node->setInnerTargetRect(QRectF(0, 0, width(), height()));
1010     node->update();
1011
1012     return node;
1013 }
1014
1015 void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeData &value)
1016 {
1017     if (change == QQuickItem::ItemSceneChange && m_sourceItem) {
1018         // See comment in QQuickShaderEffectSource::setSourceItem().
1019         if (value.canvas)
1020             QQuickItemPrivate::get(m_sourceItem)->refCanvas(value.canvas);
1021         else
1022             QQuickItemPrivate::get(m_sourceItem)->derefCanvas();
1023     }
1024     QQuickItem::itemChange(change, value);
1025 }
1026
1027 QT_END_NAMESPACE