Doc: Grouped Qt Quick types into several groups
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickshadereffect.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 <private/qquickshadereffect_p.h>
43 #include <private/qquickshadereffectnode_p.h>
44
45 #include <QtQuick/qsgmaterial.h>
46 #include "qquickitem_p.h"
47
48 #include <QtQuick/private/qsgcontext_p.h>
49 #include <QtQuick/qsgtextureprovider.h>
50 #include "qquickcanvas.h"
51
52 #include "qquickimage_p.h"
53 #include "qquickshadereffectsource_p.h"
54
55 #include <QtCore/qsignalmapper.h>
56 #include <QtGui/qopenglframebufferobject.h>
57
58 QT_BEGIN_NAMESPACE
59
60 static const char qt_default_vertex_code[] =
61     "uniform highp mat4 qt_Matrix;                                  \n"
62     "attribute highp vec4 qt_Vertex;                                \n"
63     "attribute highp vec2 qt_MultiTexCoord0;                        \n"
64     "varying highp vec2 qt_TexCoord0;                               \n"
65     "void main() {                                                  \n"
66     "    qt_TexCoord0 = qt_MultiTexCoord0;                          \n"
67     "    gl_Position = qt_Matrix * qt_Vertex;                       \n"
68     "}";
69
70 static const char qt_default_fragment_code[] =
71     "varying highp vec2 qt_TexCoord0;                                   \n"
72     "uniform sampler2D source;                                          \n"
73     "uniform lowp float qt_Opacity;                                     \n"
74     "void main() {                                                      \n"
75     "    gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;   \n"
76     "}";
77
78 static const char qt_position_attribute_name[] = "qt_Vertex";
79 static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
80
81 const char *qtPositionAttributeName()
82 {
83     return qt_position_attribute_name;
84 }
85
86 const char *qtTexCoordAttributeName()
87 {
88     return qt_texcoord_attribute_name;
89 }
90
91 namespace {
92
93     enum VariableQualifier {
94         AttributeQualifier,
95         UniformQualifier
96     };
97
98     inline bool qt_isalpha(char c)
99     {
100         char ch = c | 0x20;
101         return (ch >= 'a' && ch <= 'z') || c == '_';
102     }
103
104     inline bool qt_isalnum(char c)
105     {
106         return qt_isalpha(c) || (c >= '0' && c <= '9');
107     }
108
109     inline bool qt_isspace(char c)
110     {
111         return c == ' ' || (c >= 0x09 && c <= 0x0d);
112     }
113
114     // Returns -1 if not found, returns index to first character after the name if found.
115     int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
116                                int &typeIndex, int &typeLength,
117                                int &nameIndex, int &nameLength)
118     {
119         enum Identifier {
120             QualifierIdentifier, // Base state
121             PrecisionIdentifier,
122             TypeIdentifier,
123             NameIdentifier
124         };
125         Identifier expected = QualifierIdentifier;
126         bool compilerDirectiveExpected = index == 0;
127
128         while (index < length) {
129             // Skip whitespace.
130             while (qt_isspace(s[index])) {
131                 compilerDirectiveExpected |= s[index] == '\n';
132                 ++index;
133             }
134
135             if (qt_isalpha(s[index])) {
136                 // Read identifier.
137                 int idIndex = index;
138                 ++index;
139                 while (qt_isalnum(s[index]))
140                     ++index;
141                 int idLength = index - idIndex;
142
143                 const int attrLen = sizeof("attribute") - 1;
144                 const int uniLen = sizeof("uniform") - 1;
145                 const int loLen = sizeof("lowp") - 1;
146                 const int medLen = sizeof("mediump") - 1;
147                 const int hiLen = sizeof("highp") - 1;
148
149                 switch (expected) {
150                 case QualifierIdentifier:
151                     if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
152                         decl = AttributeQualifier;
153                         expected = PrecisionIdentifier;
154                     } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
155                         decl = UniformQualifier;
156                         expected = PrecisionIdentifier;
157                     }
158                     break;
159                 case PrecisionIdentifier:
160                     if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
161                             || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
162                             || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
163                     {
164                         expected = TypeIdentifier;
165                         break;
166                     }
167                     // Fall through.
168                 case TypeIdentifier:
169                     typeIndex = idIndex;
170                     typeLength = idLength;
171                     expected = NameIdentifier;
172                     break;
173                 case NameIdentifier:
174                     nameIndex = idIndex;
175                     nameLength = idLength;
176                     return index; // Attribute or uniform declaration found. Return result.
177                 default:
178                     break;
179                 }
180             } else if (s[index] == '#' && compilerDirectiveExpected) {
181                 // Skip compiler directives.
182                 ++index;
183                 while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
184                     ++index;
185             } else if (s[index] == '/' && s[index + 1] == '/') {
186                 // Skip comments.
187                 index += 2;
188                 while (index < length && s[index] != '\n')
189                     ++index;
190             } else if (s[index] == '/' && s[index + 1] == '*') {
191                 // Skip comments.
192                 index += 2;
193                 while (index < length && (s[index] != '*' || s[index + 1] != '/'))
194                     ++index;
195                 if (index < length)
196                     index += 2; // Skip star-slash.
197             } else {
198                 expected = QualifierIdentifier;
199                 ++index;
200             }
201             compilerDirectiveExpected = false;
202         }
203         return -1;
204     }
205 }
206
207
208
209 QQuickShaderEffectCommon::~QQuickShaderEffectCommon()
210 {
211     for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
212         qDeleteAll(signalMappers[shaderType]);
213 }
214
215 void QQuickShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
216 {
217     for (int i = 0; i < uniformData[shaderType].size(); ++i) {
218         if (signalMappers[shaderType].at(i) == 0)
219             continue;
220         const UniformData &d = uniformData[shaderType].at(i);
221         QSignalMapper *mapper = signalMappers[shaderType].at(i);
222         QObject::disconnect(item, 0, mapper, SLOT(map()));
223         QObject::disconnect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int)));
224         if (d.specialType == UniformData::Sampler) {
225             QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
226             if (source) {
227                 if (item->canvas())
228                     QQuickItemPrivate::get(source)->derefCanvas();
229                 QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
230             }
231         }
232     }
233 }
234
235 void QQuickShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
236 {
237     for (int i = 0; i < uniformData[shaderType].size(); ++i) {
238         if (signalMappers[shaderType].at(i) == 0)
239             continue;
240         const UniformData &d = uniformData[shaderType].at(i);
241         int pi = item->metaObject()->indexOfProperty(d.name.constData());
242         if (pi >= 0) {
243             QMetaProperty mp = item->metaObject()->property(pi);
244             if (!mp.hasNotifySignal())
245                 qWarning("QQuickShaderEffect: property '%s' does not have notification method!", d.name.constData());
246             const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
247             QSignalMapper *mapper = signalMappers[shaderType].at(i);
248             QObject::connect(item, signalName, mapper, SLOT(map()));
249             QObject::connect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int)));
250         } else {
251             // If the source is set via a dynamic property, like the layer is, then we need this
252             // check to disable the warning.
253             if (!item->property(d.name.constData()).isValid())
254                 qWarning("QQuickShaderEffect: '%s' does not have a matching property!", d.name.constData());
255         }
256
257         if (d.specialType == UniformData::Sampler) {
258             QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
259             if (source) {
260                 if (item->canvas())
261                     QQuickItemPrivate::get(source)->refCanvas(item->canvas());
262                 QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
263             }
264         }
265     }
266 }
267
268 void QQuickShaderEffectCommon::updateParseLog(bool ignoreAttributes)
269 {
270     parseLog.clear();
271     if (!ignoreAttributes) {
272         if (!attributes.contains(qt_position_attribute_name)) {
273             parseLog += QLatin1String("Warning: Missing reference to \'");
274             parseLog += QLatin1String(qt_position_attribute_name);
275             parseLog += QLatin1String("\'.\n");
276         }
277         if (!attributes.contains(qt_texcoord_attribute_name)) {
278             parseLog += QLatin1String("Warning: Missing reference to \'");
279             parseLog += QLatin1String(qt_texcoord_attribute_name);
280             parseLog += QLatin1String("\'.\n");
281         }
282     }
283     bool respectsMatrix = false;
284     bool respectsOpacity = false;
285     for (int i = 0; i < uniformData[Key::VertexShader].size(); ++i)
286         respectsMatrix |= uniformData[Key::VertexShader].at(i).specialType == UniformData::Matrix;
287     for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
288         for (int i = 0; i < uniformData[shaderType].size(); ++i)
289             respectsOpacity |= uniformData[shaderType].at(i).specialType == UniformData::Opacity;
290     }
291     if (!respectsMatrix)
292         parseLog += QLatin1String("Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n");
293     if (!respectsOpacity)
294         parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n");
295 }
296
297 void QQuickShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code)
298 {
299     int index = 0;
300     int typeIndex = -1;
301     int typeLength = 0;
302     int nameIndex = -1;
303     int nameLength = 0;
304     const char *s = code.constData();
305     VariableQualifier decl = AttributeQualifier;
306     while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
307                                            nameIndex, nameLength)) != -1)
308     {
309         if (decl == AttributeQualifier) {
310             if (shaderType == Key::VertexShader)
311                 attributes.append(QByteArray(s + nameIndex, nameLength));
312         } else {
313             Q_ASSERT(decl == UniformQualifier);
314
315             const int sampLen = sizeof("sampler2D") - 1;
316             const int opLen = sizeof("qt_Opacity") - 1;
317             const int matLen = sizeof("qt_Matrix") - 1;
318
319             UniformData d;
320             QSignalMapper *mapper = 0;
321             d.name = QByteArray(s + nameIndex, nameLength);
322             if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
323                 d.specialType = UniformData::Opacity;
324             } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
325                 d.specialType = UniformData::Matrix;
326             } else {
327                 mapper = new QSignalMapper;
328                 mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16));
329                 d.value = item->property(d.name.constData());
330                 bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
331                 d.specialType = sampler ? UniformData::Sampler : UniformData::None;
332             }
333             uniformData[shaderType].append(d);
334             signalMappers[shaderType].append(mapper);
335         }
336     }
337 }
338
339 void QQuickShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType)
340 {
341     disconnectPropertySignals(item, shaderType);
342     qDeleteAll(signalMappers[shaderType]);
343     uniformData[shaderType].clear();
344     signalMappers[shaderType].clear();
345     if (shaderType == Key::VertexShader)
346         attributes.clear();
347
348     const QByteArray &code = source.sourceCode[shaderType];
349     if (code.isEmpty()) {
350         // Optimize for default code.
351         if (shaderType == Key::VertexShader) {
352             attributes.append(QByteArray(qt_position_attribute_name));
353             attributes.append(QByteArray(qt_texcoord_attribute_name));
354             UniformData d;
355             d.name = "qt_Matrix";
356             d.specialType = UniformData::Matrix;
357             uniformData[Key::VertexShader].append(d);
358             signalMappers[Key::VertexShader].append(0);
359         } else if (shaderType == Key::FragmentShader) {
360             UniformData d;
361             d.name = "qt_Opacity";
362             d.specialType = UniformData::Opacity;
363             uniformData[Key::FragmentShader].append(d);
364             signalMappers[Key::FragmentShader].append(0);
365             QSignalMapper *mapper = new QSignalMapper;
366             mapper->setMapping(item, 1 | (Key::FragmentShader << 16));
367             const char *sourceName = "source";
368             d.name = sourceName;
369             d.value = item->property(sourceName);
370             d.specialType = UniformData::Sampler;
371             uniformData[Key::FragmentShader].append(d);
372             signalMappers[Key::FragmentShader].append(mapper);
373         }
374     } else {
375         lookThroughShaderCode(item, shaderType, code);
376     }
377
378     connectPropertySignals(item, shaderType);
379 }
380
381 void QQuickShaderEffectCommon::updateMaterial(QQuickShaderEffectNode *node,
382                                               QQuickShaderEffectMaterial *material,
383                                               bool updateUniforms, bool updateUniformValues,
384                                               bool updateTextureProviders)
385 {
386     if (updateUniforms) {
387         for (int i = 0; i < material->textureProviders.size(); ++i) {
388             QSGTextureProvider *t = material->textureProviders.at(i).second;
389             if (t) {
390                 QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
391                 QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
392             }
393         }
394         material->textureProviders.clear();
395
396         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
397             for (int i = 0; i < uniformData[shaderType].size(); ++i) {
398                 const UniformData &d = uniformData[shaderType].at(i);
399                 // First make room in the textureProviders array. Set to proper value further down.
400                 if (d.specialType == UniformData::Sampler)
401                     material->textureProviders.append(qMakePair(d.name, (QSGTextureProvider *)0));
402             }
403             material->uniforms[shaderType] = uniformData[shaderType];
404         }
405         updateUniformValues = false;
406         updateTextureProviders = true;
407     }
408
409     if (updateUniformValues) {
410         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
411             Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size());
412             for (int i = 0; i < uniformData[shaderType].size(); ++i)
413                 material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value;
414         }
415     }
416
417     if (updateTextureProviders) {
418         int index = 0;
419         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
420             for (int i = 0; i < uniformData[shaderType].size(); ++i) {
421                 const UniformData &d = uniformData[shaderType].at(i);
422                 if (d.specialType != UniformData::Sampler)
423                     continue;
424                 Q_ASSERT(material->textureProviders.at(index).first == d.name);
425                 QSGTextureProvider *oldProvider = material->textureProviders.at(index).second;
426                 QSGTextureProvider *newProvider = 0;
427                 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
428                 if (source && source->isTextureProvider())
429                     newProvider = source->textureProvider();
430                 if (newProvider != oldProvider) {
431                     if (oldProvider) {
432                         QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
433                         QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
434                     }
435                     if (newProvider) {
436                         Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
437                                    "QQuickShaderEffect::updatePaintNode",
438                                    "Texture provider must belong to the rendering thread");
439                         QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
440                         QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
441                     } else {
442                         const char *typeName = source ? source->metaObject()->className() : d.value.typeName();
443                         qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).",
444                                  d.name.constData(), typeName);
445                     }
446                     material->textureProviders[index].second = newProvider;
447                 }
448                 ++index;
449             }
450         }
451         Q_ASSERT(index == material->textureProviders.size());
452     }
453 }
454
455 void QQuickShaderEffectCommon::updateCanvas(QQuickCanvas *canvas)
456 {
457     // See comment in QQuickShaderEffectCommon::propertyChanged().
458     if (canvas) {
459         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
460             for (int i = 0; i < uniformData[shaderType].size(); ++i) {
461                 const UniformData &d = uniformData[shaderType].at(i);
462                 if (d.specialType == UniformData::Sampler) {
463                     QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
464                     if (source)
465                         QQuickItemPrivate::get(source)->refCanvas(canvas);
466                 }
467             }
468         }
469     } else {
470         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
471             for (int i = 0; i < uniformData[shaderType].size(); ++i) {
472                 const UniformData &d = uniformData[shaderType].at(i);
473                 if (d.specialType == UniformData::Sampler) {
474                     QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
475                     if (source)
476                         QQuickItemPrivate::get(source)->derefCanvas();
477                 }
478             }
479         }
480     }
481 }
482
483 void QQuickShaderEffectCommon::sourceDestroyed(QObject *object)
484 {
485     for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
486         for (int i = 0; i < uniformData[shaderType].size(); ++i) {
487             UniformData &d = uniformData[shaderType][i];
488             if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) {
489                 if (qvariant_cast<QObject *>(d.value) == object)
490                     d.value = QVariant();
491             }
492         }
493     }
494 }
495
496
497 void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
498                                                bool *textureProviderChanged)
499 {
500     Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
501     int index = mappedId & 0xffff;
502     UniformData &d = uniformData[shaderType][index];
503     if (d.specialType == UniformData::Sampler) {
504         QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
505         if (source) {
506             if (item->canvas())
507                 QQuickItemPrivate::get(source)->derefCanvas();
508             QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
509         }
510
511         d.value = item->property(d.name.constData());
512
513         source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
514         if (source) {
515             // 'source' needs a canvas to get a scene graph node. It usually gets one through its
516             // parent, but if the source item is "inline" rather than a reference -- i.e.
517             // "property variant source: Image { }" instead of "property variant source: foo" -- it
518             // will not get a parent. In those cases, 'source' should get the canvas from 'item'.
519             if (item->canvas())
520                 QQuickItemPrivate::get(source)->refCanvas(item->canvas());
521             QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
522         }
523         if (textureProviderChanged)
524             *textureProviderChanged = true;
525     } else {
526         d.value = item->property(d.name.constData());
527         if (textureProviderChanged)
528             *textureProviderChanged = false;
529     }
530 }
531
532
533 /*!
534     \qmlclass ShaderEffect QQuickShaderEffect
535     \inqmlmodule QtQuick 2
536     \inherits Item
537     \ingroup qtquick-shaders
538     \brief Applies custom shaders to a rectangle
539
540     The ShaderEffect element applies a custom OpenGL
541     \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
542     rectangle. It allows you to write effects such as drop shadow, blur,
543     colorize and page curl directly in QML.
544
545     There are two types of input to the \l vertexShader:
546     uniform variables and attributes. Some are predefined:
547     \list
548     \li uniform mat4 qt_Matrix - combined transformation
549        matrix, the product of the matrices from the root item to this
550        ShaderEffect, and an orthogonal projection.
551     \li uniform float qt_Opacity - combined opacity, the product of the
552        opacities from the root item to this ShaderEffect.
553     \li attribute vec4 qt_Vertex - vertex position, the top-left vertex has
554        position (0, 0), the bottom-right (\l{Item::width}{width},
555        \l{Item::height}{height}).
556     \li attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left
557        coordinate is (0, 0), the bottom-right (1, 1).
558     \endlist
559
560     In addition, any property that can be mapped to an OpenGL Shading Language
561     (GLSL) type is available as a uniform variable. The following list shows
562     how properties are mapped to GLSL uniform variables:
563     \list
564     \li bool, int, qreal -> bool, int, float - If the type in the shader is not
565        the same as in QML, the value is converted automatically.
566     \li QColor -> vec4 - When colors are passed to the shader, they are first
567        premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes
568        vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example.
569     \li QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in
570        the shader.
571     \li QPoint, QPointF, QSize, QSizeF -> vec2
572     \li QVector3D -> vec3
573     \li QTransform -> mat4
574     \li \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left
575        corner, and the color values are premultiplied.
576     \endlist
577
578     The output from the \l fragmentShader should be premultiplied. If
579     \l blending is enabled, source-over blending is used. However, additive
580     blending can be achieved by outputting zero in the alpha channel.
581
582     \row
583     \li \image declarative-shadereffectitem.png
584     \li \qml
585         import QtQuick 2.0
586
587         Rectangle {
588             width: 200; height: 100
589             Row {
590                 Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
591                 ShaderEffect {
592                     width: 100; height: 100
593                     property variant src: img
594                     vertexShader: "
595                         uniform highp mat4 qt_Matrix;
596                         attribute highp vec4 qt_Vertex;
597                         attribute highp vec2 qt_MultiTexCoord0;
598                         varying highp vec2 coord;
599                         void main() {
600                             coord = qt_MultiTexCoord0;
601                             gl_Position = qt_Matrix * qt_Vertex;
602                         }"
603                     fragmentShader: "
604                         varying highp vec2 coord;
605                         uniform sampler2D src;
606                         uniform lowp float qt_Opacity;
607                         void main() {
608                             lowp vec4 tex = texture2D(src, coord);
609                             gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity;
610                         }"
611                 }
612             }
613         }
614         \endqml
615     \endrow
616
617     By default, the ShaderEffect consists of four vertices, one for each
618     corner. For non-linear vertex transformations, like page curl, you can
619     specify a fine grid of vertices by specifying a \l mesh resolution.
620
621     \note Scene Graph textures have origin in the top-left corner rather than
622     bottom-left which is common in OpenGL.
623 */
624
625 QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
626     : QQuickItem(parent)
627     , m_meshResolution(1, 1)
628     , m_mesh(0)
629     , m_cullMode(NoCulling)
630     , m_status(Uncompiled)
631     , m_blending(true)
632     , m_dirtyUniforms(true)
633     , m_dirtyUniformValues(true)
634     , m_dirtyTextureProviders(true)
635     , m_dirtyProgram(true)
636     , m_dirtyParseLog(true)
637     , m_dirtyMesh(true)
638     , m_dirtyGeometry(true)
639 {
640     setFlag(QQuickItem::ItemHasContents);
641 }
642
643 QQuickShaderEffect::~QQuickShaderEffect()
644 {
645     for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
646         m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType));
647 }
648
649 /*!
650     \qmlproperty string QtQuick2::ShaderEffect::fragmentShader
651
652     This property holds the fragment shader's GLSL source code.
653     The default shader passes the texture coordinate along to the fragment
654     shader as "varying highp vec2 qt_TexCoord0".
655 */
656
657 void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
658 {
659     if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
660         return;
661     m_common.source.sourceCode[Key::FragmentShader] = code;
662     m_dirtyProgram = true;
663     m_dirtyParseLog = true;
664
665     if (isComponentComplete())
666         m_common.updateShader(this, Key::FragmentShader);
667
668     update();
669     if (m_status != Uncompiled) {
670         m_status = Uncompiled;
671         emit statusChanged();
672     }
673     emit fragmentShaderChanged();
674 }
675
676 /*!
677     \qmlproperty string QtQuick2::ShaderEffect::vertexShader
678
679     This property holds the vertex shader's GLSL source code.
680     The default shader expects the texture coordinate to be passed from the
681     vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
682     sampler2D named "source".
683 */
684
685 void QQuickShaderEffect::setVertexShader(const QByteArray &code)
686 {
687     if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
688         return;
689     m_common.source.sourceCode[Key::VertexShader] = code;
690     m_dirtyProgram = true;
691     m_dirtyParseLog = true;
692
693     if (isComponentComplete())
694         m_common.updateShader(this, Key::VertexShader);
695
696     update();
697     if (m_status != Uncompiled) {
698         m_status = Uncompiled;
699         emit statusChanged();
700     }
701     emit vertexShaderChanged();
702 }
703
704 /*!
705     \qmlproperty bool QtQuick2::ShaderEffect::blending
706
707     If this property is true, the output from the \l fragmentShader is blended
708     with the background using source-over blend mode. If false, the background
709     is disregarded. Blending decreases the performance, so you should set this
710     property to false when blending is not needed. The default value is true.
711 */
712
713 void QQuickShaderEffect::setBlending(bool enable)
714 {
715     if (blending() == enable)
716         return;
717
718     m_blending = enable;
719     update();
720
721     emit blendingChanged();
722 }
723
724 /*!
725     \qmlproperty variant QtQuick2::ShaderEffect::mesh
726
727     This property defines the mesh used to draw the ShaderEffect. It can hold
728     any mesh object deriving from \l QQuickShaderEffectMesh, such as \l GridMesh.
729     If a size value is assigned to this property, the ShaderEffect implicitly
730     uses a \l GridMesh with the value as
731     \l{GridMesh::resolution}{mesh resolution}. By default, this property is
732     the size 1x1.
733
734     \sa GridMesh
735 */
736
737 QVariant QQuickShaderEffect::mesh() const
738 {
739     return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
740                   : qVariantFromValue(m_meshResolution);
741 }
742
743 void QQuickShaderEffect::setMesh(const QVariant &mesh)
744 {
745     QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
746     if (newMesh && newMesh == m_mesh)
747         return;
748     if (m_mesh)
749         disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
750     m_mesh = newMesh;
751     if (m_mesh) {
752         connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
753     } else {
754         if (mesh.canConvert<QSize>()) {
755             m_meshResolution = mesh.toSize();
756         } else {
757             QList<QByteArray> res = mesh.toByteArray().split('x');
758             bool ok = res.size() == 2;
759             if (ok) {
760                 int w = res.at(0).toInt(&ok);
761                 if (ok) {
762                     int h = res.at(1).toInt(&ok);
763                     if (ok)
764                         m_meshResolution = QSize(w, h);
765                 }
766             }
767             if (!ok)
768                 qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
769         }
770         m_defaultMesh.setResolution(m_meshResolution);
771     }
772
773     m_dirtyMesh = true;
774     m_dirtyParseLog = true;
775     update();
776     emit meshChanged();
777 }
778
779 /*!
780     \qmlproperty enumeration QtQuick2::ShaderEffect::cullMode
781
782     This property defines which sides of the element should be visible.
783
784     \list
785     \li ShaderEffect.NoCulling - Both sides are visible
786     \li ShaderEffect.BackFaceCulling - only front side is visible
787     \li ShaderEffect.FrontFaceCulling - only back side is visible
788     \endlist
789
790     The default is NoCulling.
791 */
792
793 void QQuickShaderEffect::setCullMode(CullMode face)
794 {
795     if (face == m_cullMode)
796         return;
797     m_cullMode = face;
798     update();
799     emit cullModeChanged();
800 }
801
802 QString QQuickShaderEffect::parseLog()
803 {
804     if (m_dirtyParseLog) {
805         m_common.updateParseLog(m_mesh != 0);
806         m_dirtyParseLog = false;
807     }
808     return m_common.parseLog;
809 }
810
811 bool QQuickShaderEffect::event(QEvent *event)
812 {
813     if (event->type() == QEvent::DynamicPropertyChange) {
814         QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
815         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
816             for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
817                 if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) {
818                     bool textureProviderChanged;
819                     m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged);
820                     m_dirtyTextureProviders |= textureProviderChanged;
821                     m_dirtyUniformValues = true;
822                     update();
823                 }
824             }
825         }
826     }
827     return QQuickItem::event(event);
828 }
829
830 /*!
831     \qmlproperty enumeration QtQuick2::ShaderEffect::status
832
833     This property tells the current status of the OpenGL shader program.
834
835     \list
836     \li ShaderEffect.Compiled - the shader program was successfully compiled and linked.
837     \li ShaderEffect.Uncompiled - the shader program has not yet been compiled.
838     \li ShaderEffect.Error - the shader program failed to compile or link.
839     \endlist
840
841     When setting the fragment or vertex shader source code, the status will become Uncompiled.
842     The first time the ShaderEffect is rendered with new shader source code, the shaders are
843     compiled and linked, and the status is updated to Compiled or Error.
844
845     \sa log
846 */
847
848 /*!
849     \qmlproperty string QtQuick2::ShaderEffect::log
850
851     This property holds a log of warnings and errors from the latest attempt at compiling and
852     linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
853     or Error.
854
855     \sa status
856 */
857
858 void QQuickShaderEffect::updateGeometry()
859 {
860     m_dirtyGeometry = true;
861     update();
862 }
863
864 void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
865 {
866     m_log = parseLog() + log;
867     m_status = Status(status);
868     emit logChanged();
869     emit statusChanged();
870 }
871
872 void QQuickShaderEffect::sourceDestroyed(QObject *object)
873 {
874     m_common.sourceDestroyed(object);
875 }
876
877
878 void QQuickShaderEffect::propertyChanged(int mappedId)
879 {
880     bool textureProviderChanged;
881     m_common.propertyChanged(this, mappedId, &textureProviderChanged);
882     m_dirtyTextureProviders |= textureProviderChanged;
883     m_dirtyUniformValues = true;
884     update();
885 }
886
887 void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
888 {
889     m_dirtyGeometry = true;
890     QQuickItem::geometryChanged(newGeometry, oldGeometry);
891 }
892
893 QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
894 {
895     QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
896
897     // In the case of a bad vertex shader, don't try to create a node...
898     if (m_common.attributes.isEmpty()) {
899         if (node)
900             delete node;
901         return 0;
902     }
903
904     if (!node) {
905         node = new QQuickShaderEffectNode;
906         node->setMaterial(new QQuickShaderEffectMaterial(node));
907         node->setFlag(QSGNode::OwnsMaterial, true);
908         m_dirtyProgram = true;
909         m_dirtyUniforms = true;
910         m_dirtyGeometry = true;
911         connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
912     }
913
914     if (m_dirtyMesh) {
915         node->setGeometry(0);
916         m_dirtyMesh = false;
917         m_dirtyGeometry = true;
918     }
919
920     if (m_dirtyGeometry) {
921         node->setFlag(QSGNode::OwnsGeometry, false);
922         QSGGeometry *geometry = node->geometry();
923         QRectF rect(0, 0, width(), height());
924         QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
925
926         geometry = mesh->updateGeometry(geometry, m_common.attributes, rect);
927         if (!geometry) {
928             QString log = mesh->log();
929             if (!log.isNull()) {
930                 m_log = parseLog();
931                 m_log += QLatin1String("*** Mesh ***\n");
932                 m_log += log;
933                 m_status = Error;
934                 emit logChanged();
935                 emit statusChanged();
936             }
937             delete node;
938             return 0;
939         }
940
941         node->setGeometry(geometry);
942         node->setFlag(QSGNode::OwnsGeometry, true);
943
944         m_dirtyGeometry = false;
945     }
946
947     QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material());
948
949     // Update blending
950     if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
951         material->setFlag(QSGMaterial::Blending, m_blending);
952         node->markDirty(QSGNode::DirtyMaterial);
953     }
954
955     if (int(material->cullMode) != int(m_cullMode)) {
956         material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode);
957         node->markDirty(QSGNode::DirtyMaterial);
958     }
959
960     if (m_dirtyProgram) {
961         Key s = m_common.source;
962         if (s.sourceCode[Key::FragmentShader].isEmpty())
963             s.sourceCode[Key::FragmentShader] = qt_default_fragment_code;
964         if (s.sourceCode[Key::VertexShader].isEmpty())
965             s.sourceCode[Key::VertexShader] = qt_default_vertex_code;
966         s.className = metaObject()->className();
967
968         material->setProgramSource(s);
969         material->attributes = m_common.attributes;
970         node->markDirty(QSGNode::DirtyMaterial);
971         m_dirtyProgram = false;
972         m_dirtyUniforms = true;
973     }
974
975     if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) {
976         m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues,
977                                 m_dirtyTextureProviders);
978         node->markDirty(QSGNode::DirtyMaterial);
979         m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
980     }
981
982     return node;
983 }
984
985 void QQuickShaderEffect::componentComplete()
986 {
987     m_common.updateShader(this, Key::VertexShader);
988     m_common.updateShader(this, Key::FragmentShader);
989     QQuickItem::componentComplete();
990 }
991
992 void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
993 {
994     if (change == QQuickItem::ItemSceneChange)
995         m_common.updateCanvas(value.canvas);
996     QQuickItem::itemChange(change, value);
997 }
998
999 QT_END_NAMESPACE