be3023036b32dfa4fce807a4f01a77be37e2aac1
[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             const int srLen = sizeof("qt_SubRect_") - 1;
319
320             UniformData d;
321             QSignalMapper *mapper = 0;
322             d.name = QByteArray(s + nameIndex, nameLength);
323             if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
324                 d.specialType = UniformData::Opacity;
325             } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
326                 d.specialType = UniformData::Matrix;
327             } else if (nameLength > srLen && qstrncmp("qt_SubRect_", s + nameIndex, srLen) == 0) {
328                 d.specialType = UniformData::SubRect;
329             } else {
330                 mapper = new QSignalMapper;
331                 mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16));
332                 d.value = item->property(d.name.constData());
333                 bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
334                 d.specialType = sampler ? UniformData::Sampler : UniformData::None;
335             }
336             uniformData[shaderType].append(d);
337             signalMappers[shaderType].append(mapper);
338         }
339     }
340 }
341
342 void QQuickShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType)
343 {
344     disconnectPropertySignals(item, shaderType);
345     qDeleteAll(signalMappers[shaderType]);
346     uniformData[shaderType].clear();
347     signalMappers[shaderType].clear();
348     if (shaderType == Key::VertexShader)
349         attributes.clear();
350
351     const QByteArray &code = source.sourceCode[shaderType];
352     if (code.isEmpty()) {
353         // Optimize for default code.
354         if (shaderType == Key::VertexShader) {
355             attributes.append(QByteArray(qt_position_attribute_name));
356             attributes.append(QByteArray(qt_texcoord_attribute_name));
357             UniformData d;
358             d.name = "qt_Matrix";
359             d.specialType = UniformData::Matrix;
360             uniformData[Key::VertexShader].append(d);
361             signalMappers[Key::VertexShader].append(0);
362         } else if (shaderType == Key::FragmentShader) {
363             UniformData d;
364             d.name = "qt_Opacity";
365             d.specialType = UniformData::Opacity;
366             uniformData[Key::FragmentShader].append(d);
367             signalMappers[Key::FragmentShader].append(0);
368             QSignalMapper *mapper = new QSignalMapper;
369             mapper->setMapping(item, 1 | (Key::FragmentShader << 16));
370             const char *sourceName = "source";
371             d.name = sourceName;
372             d.value = item->property(sourceName);
373             d.specialType = UniformData::Sampler;
374             uniformData[Key::FragmentShader].append(d);
375             signalMappers[Key::FragmentShader].append(mapper);
376         }
377     } else {
378         lookThroughShaderCode(item, shaderType, code);
379     }
380
381     connectPropertySignals(item, shaderType);
382 }
383
384 void QQuickShaderEffectCommon::updateMaterial(QQuickShaderEffectNode *node,
385                                               QQuickShaderEffectMaterial *material,
386                                               bool updateUniforms, bool updateUniformValues,
387                                               bool updateTextureProviders)
388 {
389     if (updateUniforms) {
390         for (int i = 0; i < material->textureProviders.size(); ++i) {
391             QSGTextureProvider *t = material->textureProviders.at(i);
392             if (t) {
393                 QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
394                 QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
395             }
396         }
397
398         // First make room in the textureProviders array. Set to proper value further down.
399         int textureProviderCount = 0;
400         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
401             for (int i = 0; i < uniformData[shaderType].size(); ++i) {
402                 if (uniformData[shaderType].at(i).specialType == UniformData::Sampler)
403                     ++textureProviderCount;
404             }
405             material->uniforms[shaderType] = uniformData[shaderType];
406         }
407         material->textureProviders.fill(0, textureProviderCount);
408         updateUniformValues = false;
409         updateTextureProviders = true;
410     }
411
412     if (updateUniformValues) {
413         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
414             Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size());
415             for (int i = 0; i < uniformData[shaderType].size(); ++i)
416                 material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value;
417         }
418     }
419
420     if (updateTextureProviders) {
421         int index = 0;
422         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
423             for (int i = 0; i < uniformData[shaderType].size(); ++i) {
424                 const UniformData &d = uniformData[shaderType].at(i);
425                 if (d.specialType != UniformData::Sampler)
426                     continue;
427                 QSGTextureProvider *oldProvider = material->textureProviders.at(index);
428                 QSGTextureProvider *newProvider = 0;
429                 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
430                 if (source && source->isTextureProvider())
431                     newProvider = source->textureProvider();
432                 if (newProvider != oldProvider) {
433                     if (oldProvider) {
434                         QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
435                         QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
436                     }
437                     if (newProvider) {
438                         Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
439                                    "QQuickShaderEffect::updatePaintNode",
440                                    "Texture provider must belong to the rendering thread");
441                         QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
442                         QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
443                     } else {
444                         const char *typeName = source ? source->metaObject()->className() : d.value.typeName();
445                         qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).",
446                                  d.name.constData(), typeName);
447                     }
448                     material->textureProviders[index] = newProvider;
449                 }
450                 ++index;
451             }
452         }
453         Q_ASSERT(index == material->textureProviders.size());
454     }
455 }
456
457 void QQuickShaderEffectCommon::updateCanvas(QQuickCanvas *canvas)
458 {
459     // See comment in QQuickShaderEffectCommon::propertyChanged().
460     if (canvas) {
461         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
462             for (int i = 0; i < uniformData[shaderType].size(); ++i) {
463                 const UniformData &d = uniformData[shaderType].at(i);
464                 if (d.specialType == UniformData::Sampler) {
465                     QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
466                     if (source)
467                         QQuickItemPrivate::get(source)->refCanvas(canvas);
468                 }
469             }
470         }
471     } else {
472         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
473             for (int i = 0; i < uniformData[shaderType].size(); ++i) {
474                 const UniformData &d = uniformData[shaderType].at(i);
475                 if (d.specialType == UniformData::Sampler) {
476                     QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
477                     if (source)
478                         QQuickItemPrivate::get(source)->derefCanvas();
479                 }
480             }
481         }
482     }
483 }
484
485 void QQuickShaderEffectCommon::sourceDestroyed(QObject *object)
486 {
487     for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
488         for (int i = 0; i < uniformData[shaderType].size(); ++i) {
489             UniformData &d = uniformData[shaderType][i];
490             if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) {
491                 if (qvariant_cast<QObject *>(d.value) == object)
492                     d.value = QVariant();
493             }
494         }
495     }
496 }
497
498
499 void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
500                                                bool *textureProviderChanged)
501 {
502     Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
503     int index = mappedId & 0xffff;
504     UniformData &d = uniformData[shaderType][index];
505     if (d.specialType == UniformData::Sampler) {
506         QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
507         if (source) {
508             if (item->canvas())
509                 QQuickItemPrivate::get(source)->derefCanvas();
510             QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
511         }
512
513         d.value = item->property(d.name.constData());
514
515         source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
516         if (source) {
517             // 'source' needs a canvas to get a scene graph node. It usually gets one through its
518             // parent, but if the source item is "inline" rather than a reference -- i.e.
519             // "property variant source: Image { }" instead of "property variant source: foo" -- it
520             // will not get a parent. In those cases, 'source' should get the canvas from 'item'.
521             if (item->canvas())
522                 QQuickItemPrivate::get(source)->refCanvas(item->canvas());
523             QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
524         }
525         if (textureProviderChanged)
526             *textureProviderChanged = true;
527     } else {
528         d.value = item->property(d.name.constData());
529         if (textureProviderChanged)
530             *textureProviderChanged = false;
531     }
532 }
533
534
535 /*!
536     \qmlclass ShaderEffect QQuickShaderEffect
537     \inqmlmodule QtQuick 2
538     \inherits Item
539     \ingroup qtquick-effects
540     \brief Applies custom shaders to a rectangle
541
542     The ShaderEffect type applies a custom OpenGL
543     \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
544     rectangle. It allows you to write effects such as drop shadow, blur,
545     colorize and page curl directly in QML.
546
547     There are two types of input to the \l vertexShader:
548     uniform variables and attributes. Some are predefined:
549     \list
550     \li uniform mat4 qt_Matrix - combined transformation
551        matrix, the product of the matrices from the root item to this
552        ShaderEffect, and an orthogonal projection.
553     \li uniform float qt_Opacity - combined opacity, the product of the
554        opacities from the root item to this ShaderEffect.
555     \li attribute vec4 qt_Vertex - vertex position, the top-left vertex has
556        position (0, 0), the bottom-right (\l{Item::width}{width},
557        \l{Item::height}{height}).
558     \li attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left
559        coordinate is (0, 0), the bottom-right (1, 1).
560     \endlist
561
562     In addition, any property that can be mapped to an OpenGL Shading Language
563     (GLSL) type is available as a uniform variable. The following list shows
564     how properties are mapped to GLSL uniform variables:
565     \list
566     \li bool, int, qreal -> bool, int, float - If the type in the shader is not
567        the same as in QML, the value is converted automatically.
568     \li QColor -> vec4 - When colors are passed to the shader, they are first
569        premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes
570        vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example.
571     \li QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in
572        the shader.
573     \li QPoint, QPointF, QSize, QSizeF -> vec2
574     \li QVector3D -> vec3
575     \li QTransform -> mat4
576     \li \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left
577        corner, and the color values are premultiplied.
578     \endlist
579
580     The QML scene graph back-end may choose to allocate textures in texture
581     atlases. If a texture allocated in an atlas is passed to a ShaderEffect,
582     it is by default copied from the texture atlas into a stand-alone texture
583     so that the texture coordinates span from 0 to 1, and you get the expected
584     wrap modes. However, this will increase the memory usage. To avoid the
585     texture copy, you can for each "uniform sampler2D <name>" declare a
586     "uniform vec4 qt_SubRect_<name>" which will be assigned the texture's
587     normalized source rectangle. For stand-alone textures, the source rectangle
588     is [0, 1]x[0, 1]. For textures in an atlas, the source rectangle corresponds
589     to the part of the texture atlas where the texture is stored.
590     The correct way to calculate the texture coordinate for a texture called
591     "source" within a texture atlas is
592     "qt_SubRect_source.xy + qt_SubRect_source.zw * qt_MultiTexCoord0".
593
594     The output from the \l fragmentShader should be premultiplied. If
595     \l blending is enabled, source-over blending is used. However, additive
596     blending can be achieved by outputting zero in the alpha channel.
597
598     \table
599     \row
600     \li \image declarative-shadereffectitem.png
601     \li \qml
602         import QtQuick 2.0
603
604         Rectangle {
605             width: 200; height: 100
606             Row {
607                 Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
608                 ShaderEffect {
609                     width: 100; height: 100
610                     property variant src: img
611                     vertexShader: "
612                         uniform highp mat4 qt_Matrix;
613                         attribute highp vec4 qt_Vertex;
614                         attribute highp vec2 qt_MultiTexCoord0;
615                         varying highp vec2 coord;
616                         void main() {
617                             coord = qt_MultiTexCoord0;
618                             gl_Position = qt_Matrix * qt_Vertex;
619                         }"
620                     fragmentShader: "
621                         varying highp vec2 coord;
622                         uniform sampler2D src;
623                         uniform lowp float qt_Opacity;
624                         void main() {
625                             lowp vec4 tex = texture2D(src, coord);
626                             gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity;
627                         }"
628                 }
629             }
630         }
631         \endqml
632     \endtable
633
634     By default, the ShaderEffect consists of four vertices, one for each
635     corner. For non-linear vertex transformations, like page curl, you can
636     specify a fine grid of vertices by specifying a \l mesh resolution.
637
638     \note Scene Graph textures have origin in the top-left corner rather than
639     bottom-left which is common in OpenGL.
640 */
641
642 QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
643     : QQuickItem(parent)
644     , m_meshResolution(1, 1)
645     , m_mesh(0)
646     , m_cullMode(NoCulling)
647     , m_status(Uncompiled)
648     , m_blending(true)
649     , m_dirtyUniforms(true)
650     , m_dirtyUniformValues(true)
651     , m_dirtyTextureProviders(true)
652     , m_dirtyProgram(true)
653     , m_dirtyParseLog(true)
654     , m_dirtyMesh(true)
655     , m_dirtyGeometry(true)
656 {
657     setFlag(QQuickItem::ItemHasContents);
658 }
659
660 QQuickShaderEffect::~QQuickShaderEffect()
661 {
662     for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
663         m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType));
664 }
665
666 /*!
667     \qmlproperty string QtQuick2::ShaderEffect::fragmentShader
668
669     This property holds the fragment shader's GLSL source code.
670     The default shader passes the texture coordinate along to the fragment
671     shader as "varying highp vec2 qt_TexCoord0".
672 */
673
674 void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
675 {
676     if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
677         return;
678     m_common.source.sourceCode[Key::FragmentShader] = code;
679     m_dirtyProgram = true;
680     m_dirtyParseLog = true;
681
682     if (isComponentComplete())
683         m_common.updateShader(this, Key::FragmentShader);
684
685     update();
686     if (m_status != Uncompiled) {
687         m_status = Uncompiled;
688         emit statusChanged();
689     }
690     emit fragmentShaderChanged();
691 }
692
693 /*!
694     \qmlproperty string QtQuick2::ShaderEffect::vertexShader
695
696     This property holds the vertex shader's GLSL source code.
697     The default shader expects the texture coordinate to be passed from the
698     vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
699     sampler2D named "source".
700 */
701
702 void QQuickShaderEffect::setVertexShader(const QByteArray &code)
703 {
704     if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
705         return;
706     m_common.source.sourceCode[Key::VertexShader] = code;
707     m_dirtyProgram = true;
708     m_dirtyParseLog = true;
709
710     if (isComponentComplete())
711         m_common.updateShader(this, Key::VertexShader);
712
713     update();
714     if (m_status != Uncompiled) {
715         m_status = Uncompiled;
716         emit statusChanged();
717     }
718     emit vertexShaderChanged();
719 }
720
721 /*!
722     \qmlproperty bool QtQuick2::ShaderEffect::blending
723
724     If this property is true, the output from the \l fragmentShader is blended
725     with the background using source-over blend mode. If false, the background
726     is disregarded. Blending decreases the performance, so you should set this
727     property to false when blending is not needed. The default value is true.
728 */
729
730 void QQuickShaderEffect::setBlending(bool enable)
731 {
732     if (blending() == enable)
733         return;
734
735     m_blending = enable;
736     update();
737
738     emit blendingChanged();
739 }
740
741 /*!
742     \qmlproperty variant QtQuick2::ShaderEffect::mesh
743
744     This property defines the mesh used to draw the ShaderEffect. It can hold
745     any mesh object deriving from \l QQuickShaderEffectMesh, such as \l GridMesh.
746     If a size value is assigned to this property, the ShaderEffect implicitly
747     uses a \l GridMesh with the value as
748     \l{GridMesh::resolution}{mesh resolution}. By default, this property is
749     the size 1x1.
750
751     \sa GridMesh
752 */
753
754 QVariant QQuickShaderEffect::mesh() const
755 {
756     return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
757                   : qVariantFromValue(m_meshResolution);
758 }
759
760 void QQuickShaderEffect::setMesh(const QVariant &mesh)
761 {
762     QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
763     if (newMesh && newMesh == m_mesh)
764         return;
765     if (m_mesh)
766         disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
767     m_mesh = newMesh;
768     if (m_mesh) {
769         connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
770     } else {
771         if (mesh.canConvert<QSize>()) {
772             m_meshResolution = mesh.toSize();
773         } else {
774             QList<QByteArray> res = mesh.toByteArray().split('x');
775             bool ok = res.size() == 2;
776             if (ok) {
777                 int w = res.at(0).toInt(&ok);
778                 if (ok) {
779                     int h = res.at(1).toInt(&ok);
780                     if (ok)
781                         m_meshResolution = QSize(w, h);
782                 }
783             }
784             if (!ok)
785                 qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
786         }
787         m_defaultMesh.setResolution(m_meshResolution);
788     }
789
790     m_dirtyMesh = true;
791     m_dirtyParseLog = true;
792     update();
793     emit meshChanged();
794 }
795
796 /*!
797     \qmlproperty enumeration QtQuick2::ShaderEffect::cullMode
798
799     This property defines which sides of the item should be visible.
800
801     \list
802     \li ShaderEffect.NoCulling - Both sides are visible
803     \li ShaderEffect.BackFaceCulling - only front side is visible
804     \li ShaderEffect.FrontFaceCulling - only back side is visible
805     \endlist
806
807     The default is NoCulling.
808 */
809
810 void QQuickShaderEffect::setCullMode(CullMode face)
811 {
812     if (face == m_cullMode)
813         return;
814     m_cullMode = face;
815     update();
816     emit cullModeChanged();
817 }
818
819 QString QQuickShaderEffect::parseLog()
820 {
821     if (m_dirtyParseLog) {
822         m_common.updateParseLog(m_mesh != 0);
823         m_dirtyParseLog = false;
824     }
825     return m_common.parseLog;
826 }
827
828 bool QQuickShaderEffect::event(QEvent *event)
829 {
830     if (event->type() == QEvent::DynamicPropertyChange) {
831         QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
832         for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
833             for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
834                 if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) {
835                     bool textureProviderChanged;
836                     m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged);
837                     m_dirtyTextureProviders |= textureProviderChanged;
838                     m_dirtyUniformValues = true;
839                     update();
840                 }
841             }
842         }
843     }
844     return QQuickItem::event(event);
845 }
846
847 /*!
848     \qmlproperty enumeration QtQuick2::ShaderEffect::status
849
850     This property tells the current status of the OpenGL shader program.
851
852     \list
853     \li ShaderEffect.Compiled - the shader program was successfully compiled and linked.
854     \li ShaderEffect.Uncompiled - the shader program has not yet been compiled.
855     \li ShaderEffect.Error - the shader program failed to compile or link.
856     \endlist
857
858     When setting the fragment or vertex shader source code, the status will become Uncompiled.
859     The first time the ShaderEffect is rendered with new shader source code, the shaders are
860     compiled and linked, and the status is updated to Compiled or Error.
861
862     \sa log
863 */
864
865 /*!
866     \qmlproperty string QtQuick2::ShaderEffect::log
867
868     This property holds a log of warnings and errors from the latest attempt at compiling and
869     linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
870     or Error.
871
872     \sa status
873 */
874
875 void QQuickShaderEffect::updateGeometry()
876 {
877     m_dirtyGeometry = true;
878     update();
879 }
880
881 void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
882 {
883     m_log = parseLog() + log;
884     m_status = Status(status);
885     emit logChanged();
886     emit statusChanged();
887 }
888
889 void QQuickShaderEffect::sourceDestroyed(QObject *object)
890 {
891     m_common.sourceDestroyed(object);
892 }
893
894
895 void QQuickShaderEffect::propertyChanged(int mappedId)
896 {
897     bool textureProviderChanged;
898     m_common.propertyChanged(this, mappedId, &textureProviderChanged);
899     m_dirtyTextureProviders |= textureProviderChanged;
900     m_dirtyUniformValues = true;
901     update();
902 }
903
904 void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
905 {
906     m_dirtyGeometry = true;
907     QQuickItem::geometryChanged(newGeometry, oldGeometry);
908 }
909
910 QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
911 {
912     QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
913
914     // In the case of a bad vertex shader, don't try to create a node...
915     if (m_common.attributes.isEmpty()) {
916         if (node)
917             delete node;
918         return 0;
919     }
920
921     if (!node) {
922         node = new QQuickShaderEffectNode;
923         node->setMaterial(new QQuickShaderEffectMaterial(node));
924         node->setFlag(QSGNode::OwnsMaterial, true);
925         m_dirtyProgram = true;
926         m_dirtyUniforms = true;
927         m_dirtyGeometry = true;
928         connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
929     }
930
931     if (m_dirtyMesh) {
932         node->setGeometry(0);
933         m_dirtyMesh = false;
934         m_dirtyGeometry = true;
935     }
936
937     if (m_dirtyGeometry) {
938         node->setFlag(QSGNode::OwnsGeometry, false);
939         QSGGeometry *geometry = node->geometry();
940         QRectF rect(0, 0, width(), height());
941         QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
942
943         geometry = mesh->updateGeometry(geometry, m_common.attributes, rect);
944         if (!geometry) {
945             QString log = mesh->log();
946             if (!log.isNull()) {
947                 m_log = parseLog();
948                 m_log += QLatin1String("*** Mesh ***\n");
949                 m_log += log;
950                 m_status = Error;
951                 emit logChanged();
952                 emit statusChanged();
953             }
954             delete node;
955             return 0;
956         }
957
958         node->setGeometry(geometry);
959         node->setFlag(QSGNode::OwnsGeometry, true);
960
961         m_dirtyGeometry = false;
962     }
963
964     QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material());
965
966     // Update blending
967     if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
968         material->setFlag(QSGMaterial::Blending, m_blending);
969         node->markDirty(QSGNode::DirtyMaterial);
970     }
971
972     if (int(material->cullMode) != int(m_cullMode)) {
973         material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode);
974         node->markDirty(QSGNode::DirtyMaterial);
975     }
976
977     if (m_dirtyProgram) {
978         Key s = m_common.source;
979         if (s.sourceCode[Key::FragmentShader].isEmpty())
980             s.sourceCode[Key::FragmentShader] = qt_default_fragment_code;
981         if (s.sourceCode[Key::VertexShader].isEmpty())
982             s.sourceCode[Key::VertexShader] = qt_default_vertex_code;
983         s.className = metaObject()->className();
984
985         material->setProgramSource(s);
986         material->attributes = m_common.attributes;
987         node->markDirty(QSGNode::DirtyMaterial);
988         m_dirtyProgram = false;
989         m_dirtyUniforms = true;
990     }
991
992     if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) {
993         m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues,
994                                 m_dirtyTextureProviders);
995         node->markDirty(QSGNode::DirtyMaterial);
996         m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
997     }
998
999     return node;
1000 }
1001
1002 void QQuickShaderEffect::componentComplete()
1003 {
1004     m_common.updateShader(this, Key::VertexShader);
1005     m_common.updateShader(this, Key::FragmentShader);
1006     QQuickItem::componentComplete();
1007 }
1008
1009 void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
1010 {
1011     if (change == QQuickItem::ItemSceneChange)
1012         m_common.updateCanvas(value.canvas);
1013     QQuickItem::itemChange(change, value);
1014 }
1015
1016 QT_END_NAMESPACE