Change copyrights from Nokia to Digia
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickshadereffectsource.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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     \qmltype ShaderEffectSource
461     \instantiates QQuickShaderEffectSource
462     \inqmlmodule QtQuick 2
463     \since QtQuick 2.0
464     \inherits Item
465     \ingroup qtquick-effects
466     \brief Renders a QtQuick item into a texture and displays it
467
468     The ShaderEffectSource type renders \l sourceItem into a texture and
469     displays it in the scene. \l sourceItem is drawn into the texture as though
470     it was a fully opaque root item. Thus \l sourceItem itself can be
471     invisible, but still appear in the texture.
472
473     ShaderEffectSource can be used as:
474     \list
475     \li a texture source in a \l ShaderEffect.
476        This allows you to apply custom shader effects to any QtQuick item.
477     \li a cache for a complex item.
478        The complex item can be rendered once into the texture, which can
479        then be animated freely without the need to render the complex item
480        again every frame.
481     \li an opacity layer.
482        ShaderEffectSource allows you to apply an opacity to items as a group
483        rather than each item individually.
484     \endlist
485
486     \table
487     \row
488     \li \image declarative-shadereffectsource.png
489     \li \qml
490         import QtQuick 2.0
491
492         Rectangle {
493             width: 200
494             height: 100
495             gradient: Gradient {
496                 GradientStop { position: 0; color: "white" }
497                 GradientStop { position: 1; color: "black" }
498             }
499             Row {
500                 opacity: 0.5
501                 Item {
502                     id: foo
503                     width: 100; height: 100
504                     Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
505                     Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
506                     Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
507                 }
508                 ShaderEffectSource {
509                     width: 100; height: 100
510                     sourceItem: foo
511                 }
512             }
513         }
514         \endqml
515     \endtable
516
517     The ShaderEffectSource type 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 is the default value of smooth).
535     This will be equivalent to 4x multisampling, at the cost of lower performance
536     and higher memory use.
537
538     \warning In most cases, using a ShaderEffectSource will decrease
539     performance, and in all cases, it will increase video memory usage.
540     Rendering through a ShaderEffectSource might also lead to lower quality
541     since some OpenGL implementations support multisampled backbuffer,
542     but not multisampled framebuffer objects.
543 */
544
545 QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
546     : QQuickItem(parent)
547     , m_provider(0)
548     , m_texture(0)
549     , m_wrapMode(ClampToEdge)
550     , m_sourceItem(0)
551     , m_textureSize(0, 0)
552     , m_format(RGBA)
553     , m_live(true)
554     , m_hideSource(false)
555     , m_mipmap(false)
556     , m_recursive(false)
557     , m_grab(true)
558 {
559     setFlag(ItemHasContents);
560 }
561
562 QQuickShaderEffectSource::~QQuickShaderEffectSource()
563 {
564     if (m_texture)
565         m_texture->deleteLater();
566
567     if (m_provider)
568         m_provider->deleteLater();
569
570     if (m_sourceItem) {
571         QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem);
572         sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
573         sd->derefFromEffectItem(m_hideSource);
574         if (window())
575             sd->derefWindow();
576     }
577 }
578
579 void QQuickShaderEffectSource::ensureTexture()
580 {
581     if (m_texture)
582         return;
583
584     Q_ASSERT_X(QQuickItemPrivate::get(this)->window
585                && QQuickItemPrivate::get(this)->sceneGraphContext()
586                && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
587                "QQuickShaderEffectSource::ensureTexture",
588                "Cannot be used outside the rendering thread");
589
590     m_texture = new QQuickShaderEffectTexture(this);
591     connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update()));
592     connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted()));
593 }
594
595 QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
596 {
597     if (!m_provider) {
598         // Make sure it gets thread affinity on the rendering thread so deletion works properly..
599         Q_ASSERT_X(QQuickItemPrivate::get(this)->window
600                    && QQuickItemPrivate::get(this)->sceneGraphContext()
601                    && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
602                    "QQuickShaderEffectSource::textureProvider",
603                    "Cannot be used outside the rendering thread");
604         const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
605         const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
606         connect(m_texture, SIGNAL(updateRequested()), m_provider, SIGNAL(textureChanged()));
607         m_provider->sourceTexture = m_texture;
608     }
609     return m_provider;
610 }
611
612 /*!
613     \qmlproperty enumeration QtQuick2::ShaderEffectSource::wrapMode
614
615     This property defines the OpenGL wrap modes associated with the texture.
616     Modifying this property makes most sense when the item is used as a
617     source texture of a \l ShaderEffect.
618
619     \list
620     \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
621     \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
622     \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
623     \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
624     \endlist
625
626     \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
627     wrap mode with non-power-of-two textures.
628 */
629
630 QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
631 {
632     return m_wrapMode;
633 }
634
635 void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
636 {
637     if (mode == m_wrapMode)
638         return;
639     m_wrapMode = mode;
640     update();
641     emit wrapModeChanged();
642 }
643
644 /*!
645     \qmlproperty Item QtQuick2::ShaderEffectSource::sourceItem
646
647     This property holds the item to be rendered into the texture.
648     Setting this to null while \l live is true, will release the texture
649     resources.
650 */
651
652 QQuickItem *QQuickShaderEffectSource::sourceItem() const
653 {
654     return m_sourceItem;
655 }
656
657 void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect)
658 {
659     Q_ASSERT(item == m_sourceItem);
660     Q_UNUSED(item);
661     if (newRect.size() != oldRect.size())
662         update();
663 }
664
665 void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
666 {
667     if (item == m_sourceItem)
668         return;
669     if (m_sourceItem) {
670         QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem);
671         d->derefFromEffectItem(m_hideSource);
672         d->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
673         disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
674         if (window())
675             d->derefWindow();
676     }
677     m_sourceItem = item;
678
679     if (item) {
680         QQuickItemPrivate *d = QQuickItemPrivate::get(item);
681         // 'item' needs a window to get a scene graph node. It usually gets one through its
682         // parent, but if the source item is "inline" rather than a reference -- i.e.
683         // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent.
684         // In those cases, 'item' should get the window from 'this'.
685         if (window())
686             d->refWindow(window());
687         d->refFromEffectItem(m_hideSource);
688         d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
689         connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
690     }
691     update();
692     emit sourceItemChanged();
693 }
694
695 void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
696 {
697     Q_ASSERT(item == m_sourceItem);
698     m_sourceItem = 0;
699     update();
700     emit sourceItemChanged();
701 }
702
703
704 /*!
705     \qmlproperty rect QtQuick2::ShaderEffectSource::sourceRect
706
707     This property defines which rectangular area of the \l sourceItem to
708     render into the texture. The source rectangle can be larger than
709     \l sourceItem itself. If the rectangle is null, which is the default,
710     the whole \l sourceItem is rendered to texture.
711 */
712
713 QRectF QQuickShaderEffectSource::sourceRect() const
714 {
715     return m_sourceRect;
716 }
717
718 void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
719 {
720     if (rect == m_sourceRect)
721         return;
722     m_sourceRect = rect;
723     update();
724     emit sourceRectChanged();
725 }
726
727 /*!
728     \qmlproperty size QtQuick2::ShaderEffectSource::textureSize
729
730     This property holds the requested size of the texture. If it is empty,
731     which is the default, the size of the source rectangle is used.
732
733     \note Some platforms have a limit on how small framebuffer objects can be,
734     which means the actual texture size might be larger than the requested
735     size.
736 */
737
738 QSize QQuickShaderEffectSource::textureSize() const
739 {
740     return m_textureSize;
741 }
742
743 void QQuickShaderEffectSource::setTextureSize(const QSize &size)
744 {
745     if (size == m_textureSize)
746         return;
747     m_textureSize = size;
748     update();
749     emit textureSizeChanged();
750 }
751
752 /*!
753     \qmlproperty enumeration QtQuick2::ShaderEffectSource::format
754
755     This property defines the internal OpenGL format of the texture.
756     Modifying this property makes most sense when the item is used as a
757     source texture of a \l ShaderEffect. Depending on the OpenGL
758     implementation, this property might allow you to save some texture memory.
759
760     \list
761     \li ShaderEffectSource.Alpha - GL_ALPHA
762     \li ShaderEffectSource.RGB - GL_RGB
763     \li ShaderEffectSource.RGBA - GL_RGBA
764     \endlist
765
766     \note Some OpenGL implementations do not support the GL_ALPHA format.
767 */
768
769 QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
770 {
771     return m_format;
772 }
773
774 void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
775 {
776     if (format == m_format)
777         return;
778     m_format = format;
779     update();
780     emit formatChanged();
781 }
782
783 /*!
784     \qmlproperty bool QtQuick2::ShaderEffectSource::live
785
786     If this property is true, the texture is updated whenever the
787     \l sourceItem updates. Otherwise, it will be a frozen image, even if
788     \l sourceItem is assigned a new item. The property is true by default.
789 */
790
791 bool QQuickShaderEffectSource::live() const
792 {
793     return m_live;
794 }
795
796 void QQuickShaderEffectSource::setLive(bool live)
797 {
798     if (live == m_live)
799         return;
800     m_live = live;
801     update();
802     emit liveChanged();
803 }
804
805 /*!
806     \qmlproperty bool QtQuick2::ShaderEffectSource::hideSource
807
808     If this property is true, the \l sourceItem is hidden, though it will still
809     be rendered into the texture. As opposed to hiding the \l sourceItem by
810     setting \l{Item::visible}{visible} to false, setting this property to true
811     will not prevent mouse or keyboard input from reaching \l sourceItem.
812     The property is useful when the ShaderEffectSource is anchored on top of,
813     and meant to replace the \l sourceItem.
814 */
815
816 bool QQuickShaderEffectSource::hideSource() const
817 {
818     return m_hideSource;
819 }
820
821 void QQuickShaderEffectSource::setHideSource(bool hide)
822 {
823     if (hide == m_hideSource)
824         return;
825     if (m_sourceItem) {
826         QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
827         QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
828     }
829     m_hideSource = hide;
830     update();
831     emit hideSourceChanged();
832 }
833
834 /*!
835     \qmlproperty bool QtQuick2::ShaderEffectSource::mipmap
836
837     If this property is true, mipmaps are generated for the texture.
838
839     \note Some OpenGL ES 2 implementations do not support mipmapping of
840     non-power-of-two textures.
841 */
842
843 bool QQuickShaderEffectSource::mipmap() const
844 {
845     return m_mipmap;
846 }
847
848 void QQuickShaderEffectSource::setMipmap(bool enabled)
849 {
850     if (enabled == m_mipmap)
851         return;
852     m_mipmap = enabled;
853     update();
854     emit mipmapChanged();
855 }
856
857 /*!
858     \qmlproperty bool QtQuick2::ShaderEffectSource::recursive
859
860     Set this property to true if the ShaderEffectSource has a dependency on
861     itself. ShaderEffectSources form a dependency chain, where one
862     ShaderEffectSource can be part of the \l sourceItem of another.
863     If there is a loop in this chain, a ShaderEffectSource could end up trying
864     to render into the same texture it is using as source, which is not allowed
865     by OpenGL. When this property is set to true, an extra texture is allocated
866     so that ShaderEffectSource can keep a copy of the texture from the previous
867     frame. It can then render into one texture and use the texture from the
868     previous frame as source.
869
870     Setting both this property and \l live to true will cause the scene graph
871     to render continuously. Since the ShaderEffectSource depends on itself,
872     updating it means that it immediately becomes dirty again.
873 */
874
875 bool QQuickShaderEffectSource::recursive() const
876 {
877     return m_recursive;
878 }
879
880 void QQuickShaderEffectSource::setRecursive(bool enabled)
881 {
882     if (enabled == m_recursive)
883         return;
884     m_recursive = enabled;
885     emit recursiveChanged();
886 }
887
888 /*!
889     \qmlmethod QtQuick2::ShaderEffectSource::scheduleUpdate()
890
891     Schedules a re-rendering of the texture for the next frame.
892     Use this to update the texture when \l live is false.
893 */
894
895 void QQuickShaderEffectSource::scheduleUpdate()
896 {
897     if (m_grab)
898         return;
899     m_grab = true;
900     update();
901 }
902
903 static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
904 {
905     switch (mode) {
906     case QQuickShaderEffectSource::RepeatHorizontally:
907         *hWrap = QSGTexture::Repeat;
908         *vWrap = QSGTexture::ClampToEdge;
909         break;
910     case QQuickShaderEffectSource::RepeatVertically:
911         *vWrap = QSGTexture::Repeat;
912         *hWrap = QSGTexture::ClampToEdge;
913         break;
914     case QQuickShaderEffectSource::Repeat:
915         *hWrap = *vWrap = QSGTexture::Repeat;
916         break;
917     default:
918         // QQuickShaderEffectSource::ClampToEdge
919         *hWrap = *vWrap = QSGTexture::ClampToEdge;
920         break;
921     }
922 }
923
924
925 void QQuickShaderEffectSource::releaseResources()
926 {
927     if (m_texture) {
928         m_texture->deleteLater();
929         m_texture = 0;
930     }
931     if (m_provider) {
932         m_provider->deleteLater();
933         m_provider = 0;
934     }
935 }
936
937 QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
938 {
939     if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) {
940         if (m_texture)
941             m_texture->setItem(0);
942         delete oldNode;
943         return 0;
944     }
945
946     ensureTexture();
947
948     m_texture->setLive(m_live);
949     m_texture->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
950     QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
951                       ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
952                       : m_sourceRect;
953     m_texture->setRect(sourceRect);
954     QSize textureSize = m_textureSize.isEmpty()
955                       ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
956                       : m_textureSize;
957     Q_ASSERT(!textureSize.isEmpty());
958     QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(this));
959     const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
960     // Keep power-of-two by doubling the size.
961     while (textureSize.width() < minTextureSize.width())
962         textureSize.rwidth() *= 2;
963     while (textureSize.height() < minTextureSize.height())
964         textureSize.rheight() *= 2;
965
966     m_texture->setSize(textureSize);
967     m_texture->setRecursive(m_recursive);
968     m_texture->setFormat(GLenum(m_format));
969     m_texture->setHasMipmaps(m_mipmap);
970
971     if (m_grab)
972         m_texture->scheduleUpdate();
973     m_grab = false;
974
975     QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth
976                                             ? QSGTexture::Linear
977                                             : QSGTexture::Nearest;
978     QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
979     QSGTexture::WrapMode hWrap, vWrap;
980     get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
981
982     if (m_provider) {
983         m_provider->mipmapFiltering = mmFiltering;
984         m_provider->filtering = filtering;
985         m_provider->horizontalWrap = hWrap;
986         m_provider->verticalWrap = vWrap;
987     }
988
989     // Don't create the paint node if we're not spanning any area
990     if (width() == 0 || height() == 0) {
991         delete oldNode;
992         return 0;
993     }
994
995     QQuickShaderEffectSourceNode *node = static_cast<QQuickShaderEffectSourceNode *>(oldNode);
996     if (!node) {
997         node = new QQuickShaderEffectSourceNode;
998         node->setTexture(m_texture);
999         connect(m_texture, SIGNAL(updateRequested()), node, SLOT(markDirtyTexture()));
1000     }
1001
1002     // If live and recursive, update continuously.
1003     if (m_live && m_recursive)
1004         node->markDirty(QSGNode::DirtyMaterial);
1005
1006     node->setMipmapFiltering(mmFiltering);
1007     node->setFiltering(filtering);
1008     node->setHorizontalWrapMode(hWrap);
1009     node->setVerticalWrapMode(vWrap);
1010     node->setTargetRect(QRectF(0, 0, width(), height()));
1011     node->setInnerTargetRect(QRectF(0, 0, width(), height()));
1012     node->update();
1013
1014     return node;
1015 }
1016
1017 void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeData &value)
1018 {
1019     if (change == QQuickItem::ItemSceneChange && m_sourceItem) {
1020         // See comment in QQuickShaderEffectSource::setSourceItem().
1021         if (value.window)
1022             QQuickItemPrivate::get(m_sourceItem)->refWindow(value.window);
1023         else
1024             QQuickItemPrivate::get(m_sourceItem)->derefWindow();
1025     }
1026     QQuickItem::itemChange(change, value);
1027 }
1028
1029 QT_END_NAMESPACE