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