Adapt to Qt5 meta-object changes
[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 /*!
92     \qmlclass ShaderEffect QQuickShaderEffect
93     \inqmlmodule QtQuick 2
94     \ingroup qml-basic-visual-elements
95     \brief The ShaderEffect element applies custom shaders to a rectangle.
96     \inherits Item
97
98     The ShaderEffect element applies a custom OpenGL
99     \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
100     rectangle. It allows you to write effects such as drop shadow, blur,
101     colorize and page curl directly in QML.
102
103     There are two types of input to the \l vertexShader:
104     uniform variables and attributes. Some are predefined:
105     \list
106     \o uniform mat4 qt_Matrix - combined transformation
107        matrix, the product of the matrices from the root item to this
108        ShaderEffect, and an orthogonal projection.
109     \o uniform float qt_Opacity - combined opacity, the product of the
110        opacities from the root item to this ShaderEffect.
111     \o attribute vec4 qt_Vertex - vertex position, the top-left vertex has
112        position (0, 0), the bottom-right (\l{Item::width}{width},
113        \l{Item::height}{height}).
114     \o attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left
115        coordinate is (0, 0), the bottom-right (1, 1).
116     \endlist
117
118     In addition, any property that can be mapped to an OpenGL Shading Language
119     (GLSL) type is available as a uniform variable. The following list shows
120     how properties are mapped to GLSL uniform variables:
121     \list
122     \o bool, int, qreal -> bool, int, float - If the type in the shader is not
123        the same as in QML, the value is converted automatically.
124     \o QColor -> vec4 - When colors are passed to the shader, they are first
125        premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes
126        vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example.
127     \o QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in
128        the shader.
129     \o QPoint, QPointF, QSize, QSizeF -> vec2
130     \o QVector3D -> vec3
131     \o QTransform -> mat4
132     \o \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left
133        corner, and the color values are premultiplied.
134     \endlist
135
136     The output from the \l fragmentShader should be premultiplied. If
137     \l blending is enabled, source-over blending is used. However, additive
138     blending can be achieved by outputting zero in the alpha channel.
139
140     \row
141     \o \image declarative-shadereffectitem.png
142     \o \qml
143         import QtQuick 2.0
144
145         Rectangle {
146             width: 200; height: 100
147             Row {
148                 Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
149                 ShaderEffect {
150                     width: 100; height: 100
151                     property variant src: img
152                     vertexShader: "
153                         uniform highp mat4 qt_Matrix;
154                         attribute highp vec4 qt_Vertex;
155                         attribute highp vec2 qt_MultiTexCoord0;
156                         varying highp vec2 coord;
157                         void main() {
158                             coord = qt_MultiTexCoord0;
159                             gl_Position = qt_Matrix * qt_Vertex;
160                         }"
161                     fragmentShader: "
162                         varying highp vec2 coord;
163                         uniform sampler2D src;
164                         uniform lowp float qt_Opacity;
165                         void main() {
166                             lowp vec4 tex = texture2D(src, coord);
167                             gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity;
168                         }"
169                 }
170             }
171         }
172         \endqml
173     \endrow
174
175     By default, the ShaderEffect consists of four vertices, one for each
176     corner. For non-linear vertex transformations, like page curl, you can
177     specify a fine grid of vertices by specifying a \l mesh resolution.
178
179     \note Scene Graph textures have origin in the top-left corner rather than
180     bottom-left which is common in OpenGL.
181 */
182
183 QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
184     : QQuickItem(parent)
185     , m_meshResolution(1, 1)
186     , m_mesh(0)
187     , m_cullMode(NoCulling)
188     , m_status(Uncompiled)
189     , m_blending(true)
190     , m_dirtyData(true)
191     , m_programDirty(true)
192     , m_dirtyMesh(true)
193     , m_dirtyGeometry(true)
194     , m_complete(false)
195 {
196     setFlag(QQuickItem::ItemHasContents);
197 }
198
199 QQuickShaderEffect::~QQuickShaderEffect()
200 {
201     reset();
202 }
203
204 /*!
205     \qmlproperty string QtQuick2::ShaderEffect::fragmentShader
206
207     This property holds the fragment shader's GLSL source code.
208     The default shader passes the texture coordinate along to the fragment
209     shader as "varying highp vec2 qt_TexCoord0".
210 */
211
212 void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
213 {
214     if (m_source.fragmentCode.constData() == code.constData())
215         return;
216     m_source.fragmentCode = code;
217     update();
218     m_complete = false;
219     if (m_status != Uncompiled) {
220         m_status = Uncompiled;
221         emit statusChanged();
222     }
223     emit fragmentShaderChanged();
224 }
225
226 /*!
227     \qmlproperty string QtQuick2::ShaderEffect::vertexShader
228
229     This property holds the vertex shader's GLSL source code.
230     The default shader expects the texture coordinate to be passed from the
231     vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
232     sampler2D named "source".
233 */
234
235 void QQuickShaderEffect::setVertexShader(const QByteArray &code)
236 {
237     if (m_source.vertexCode.constData() == code.constData())
238         return;
239     m_source.vertexCode = code;
240     update();
241     m_complete = false;
242     if (m_status != Uncompiled) {
243         m_status = Uncompiled;
244         emit statusChanged();
245     }
246     emit vertexShaderChanged();
247 }
248
249 /*!
250     \qmlproperty bool QtQuick2::ShaderEffect::blending
251
252     If this property is true, the output from the \l fragmentShader is blended
253     with the background using source-over blend mode. If false, the background
254     is disregarded. Blending decreases the performance, so you should set this
255     property to false when blending is not needed. The default value is true.
256 */
257
258 void QQuickShaderEffect::setBlending(bool enable)
259 {
260     if (blending() == enable)
261         return;
262
263     m_blending = enable;
264     update();
265
266     emit blendingChanged();
267 }
268
269 /*!
270     \qmlproperty variant QtQuick2::ShaderEffect::mesh
271
272     This property defines the mesh used to draw the ShaderEffect. It can hold
273     any mesh object deriving from \l QQuickShaderEffectMesh, such as \l GridMesh.
274     If a size value is assigned to this property, the ShaderEffect implicitly
275     uses a \l GridMesh with the value as
276     \l{GridMesh::resolution}{mesh resolution}. By default, this property is
277     the size 1x1.
278
279     \sa GridMesh
280 */
281
282 QVariant QQuickShaderEffect::mesh() const
283 {
284     return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
285                   : qVariantFromValue(m_meshResolution);
286 }
287
288 void QQuickShaderEffect::setMesh(const QVariant &mesh)
289 {
290     QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qVariantValue<QObject *>(mesh));
291     if (newMesh && newMesh == m_mesh)
292         return;
293     if (m_mesh)
294         disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
295     m_mesh = newMesh;
296     if (m_mesh) {
297         connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
298     } else {
299         if (qVariantCanConvert<QSize>(mesh)) {
300             m_meshResolution = mesh.toSize();
301         } else {
302             QList<QByteArray> res = mesh.toByteArray().split('x');
303             bool ok = res.size() == 2;
304             if (ok) {
305                 int w = res.at(0).toInt(&ok);
306                 if (ok) {
307                     int h = res.at(1).toInt(&ok);
308                     if (ok)
309                         m_meshResolution = QSize(w, h);
310                 }
311             }
312             if (!ok)
313                 qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
314         }
315         m_defaultMesh.setResolution(m_meshResolution);
316     }
317
318     m_dirtyMesh = true;
319     update();
320     emit meshChanged();
321 }
322
323 /*!
324     \qmlproperty enumeration QtQuick2::ShaderEffect::cullMode
325
326     This property defines which sides of the element should be visible.
327
328     \list
329     \o ShaderEffect.NoCulling - Both sides are visible
330     \o ShaderEffect.BackFaceCulling - only front side is visible
331     \o ShaderEffect.FrontFaceCulling - only back side is visible
332     \endlist
333
334     The default is NoCulling.
335 */
336
337 void QQuickShaderEffect::setCullMode(CullMode face)
338 {
339     if (face == m_cullMode)
340         return;
341     m_cullMode = face;
342     update();
343     emit cullModeChanged();
344 }
345
346 /*!
347     \qmlproperty enumeration QtQuick2::ShaderEffect::status
348
349     This property tells the current status of the OpenGL shader program.
350
351     \list
352     \o ShaderEffect.Compiled - the shader program was successfully compiled and linked.
353     \o ShaderEffect.Uncompiled - the shader program has not yet been compiled.
354     \o ShaderEffect.Error - the shader program failed to compile or link.
355     \endlist
356
357     When setting the fragment or vertex shader source code, the status will become Uncompiled.
358     The first time the ShaderEffect is rendered with new shader source code, the shaders are
359     compiled and linked, and the status is updated to Compiled or Error.
360
361     \sa log
362 */
363
364 /*!
365     \qmlproperty string QtQuick2::ShaderEffect::log
366
367     This property holds a log of warnings and errors from the latest attempt at compiling and
368     linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
369     or Error.
370
371     \sa status
372 */
373
374 void QQuickShaderEffect::changeSource(int index)
375 {
376     Q_ASSERT(index >= 0 && index < m_sources.size());
377     QVariant v = property(m_sources.at(index).name.constData());
378     setSource(v, index);
379 }
380
381 void QQuickShaderEffect::updateData()
382 {
383     m_dirtyData = true;
384     update();
385 }
386
387 void QQuickShaderEffect::updateGeometry()
388 {
389     m_dirtyGeometry = true;
390     update();
391 }
392
393 void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
394 {
395     m_log = m_parseLog + log;
396     m_status = Status(status);
397     emit logChanged();
398     emit statusChanged();
399 }
400
401 void QQuickShaderEffect::setSource(const QVariant &var, int index)
402 {
403     Q_ASSERT(index >= 0 && index < m_sources.size());
404
405     SourceData &source = m_sources[index];
406
407     source.sourceObject = 0;
408     if (var.isNull()) {
409         return;
410     } else if (!qVariantCanConvert<QObject *>(var)) {
411         qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData());
412         return;
413     }
414
415     QObject *obj = qVariantValue<QObject *>(var);
416     if (!obj)
417         return;
418     QQuickItem *item = qobject_cast<QQuickItem *>(obj);
419     if (!item || !item->isTextureProvider()) {
420         qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]",
421                  source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className());
422         return;
423     }
424
425     source.sourceObject = item;
426
427     if (item) {
428         QQuickItemPrivate *d = QQuickItemPrivate::get(item);
429         // 'item' needs a canvas to get a scene graph node. It usually gets one through its
430         // parent, but if the source item is "inline" rather than a reference -- i.e.
431         // "property variant source: Image { }" instead of "property variant source: foo" -- it
432         // will not get a parent. In those cases, 'item' should get the canvas from 'this'.
433         if (!d->parentItem && canvas() && !d->canvas) {
434             QQuickItemPrivate::InitializationState initState;
435             initState.clear();
436             d->initCanvas(&initState, canvas());
437         }
438     }
439 }
440
441 void QQuickShaderEffect::disconnectPropertySignals()
442 {
443     disconnect(this, 0, this, SLOT(updateData()));
444     for (int i = 0; i < m_sources.size(); ++i) {
445         SourceData &source = m_sources[i];
446         disconnect(this, 0, source.mapper, 0);
447         disconnect(source.mapper, 0, this, 0);
448     }
449 }
450
451 void QQuickShaderEffect::connectPropertySignals()
452 {
453     QSet<QByteArray>::const_iterator it;
454     for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
455         int pi = metaObject()->indexOfProperty(it->constData());
456         if (pi >= 0) {
457             QMetaProperty mp = metaObject()->property(pi);
458             if (!mp.hasNotifySignal())
459                 qWarning("QQuickShaderEffect: property '%s' does not have notification method!", it->constData());
460             QByteArray signalName("2");
461             signalName.append(mp.notifySignal().methodSignature());
462             connect(this, signalName, this, SLOT(updateData()));
463         } else {
464             // If the source is set via a dynamic property, like the layer is, then we need this check
465             // to disable the warning.
466             if (property(it->constData()).isValid())
467                 continue;
468             qWarning("QQuickShaderEffect: '%s' does not have a matching property!", it->constData());
469         }
470     }
471     for (int i = 0; i < m_sources.size(); ++i) {
472         SourceData &source = m_sources[i];
473         int pi = metaObject()->indexOfProperty(source.name.constData());
474         if (pi >= 0) {
475             QMetaProperty mp = metaObject()->property(pi);
476             QByteArray signalName("2");
477             signalName.append(mp.notifySignal().methodSignature());
478             connect(this, signalName, source.mapper, SLOT(map()));
479             source.mapper->setMapping(this, i);
480             connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
481         } else {
482             // If the source is set via a dynamic property, like the layer is, then we need this check
483             // to disable the warning.
484             if (property(source.name.constData()).isValid())
485                 continue;
486             qWarning("QQuickShaderEffect: '%s' does not have a matching source!", source.name.constData());
487         }
488     }
489 }
490
491
492 void QQuickShaderEffect::ensureCompleted()
493 {
494     if (!m_complete) {
495         reset();
496         updateProperties();
497         m_complete = true;
498     }
499 }
500
501
502 void QQuickShaderEffect::reset()
503 {
504     disconnectPropertySignals();
505
506     m_source.attributeNames.clear();
507     m_source.uniformNames.clear();
508     m_source.respectsOpacity = false;
509     m_source.respectsMatrix = false;
510     m_source.className = metaObject()->className();
511
512     for (int i = 0; i < m_sources.size(); ++i) {
513         const SourceData &source = m_sources.at(i);
514         delete source.mapper;
515     }
516     m_sources.clear();
517     m_log.clear();
518     m_parseLog.clear();
519     m_programDirty = true;
520     m_dirtyMesh = true;
521 }
522
523 void QQuickShaderEffect::updateProperties()
524 {
525     if (m_source.vertexCode.isEmpty()) {
526         m_source.attributeNames.append(QByteArray(qt_position_attribute_name));
527         m_source.attributeNames.append(QByteArray(qt_texcoord_attribute_name));
528         m_source.respectsMatrix = true;
529     } else {
530         lookThroughShaderCode(m_source.vertexCode);
531     }
532     if (m_source.fragmentCode.isEmpty()) {
533         m_source.respectsOpacity = true;
534         QByteArray name("source");
535         m_source.uniformNames.insert(name);
536         SourceData d;
537         d.mapper = new QSignalMapper;
538         d.name = name;
539         d.sourceObject = 0;
540         m_sources.append(d);
541     } else {
542         lookThroughShaderCode(m_source.fragmentCode);
543     }
544
545     if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) {
546         m_parseLog += QLatin1String("Warning: Missing reference to \'");
547         m_parseLog += QLatin1String(qt_position_attribute_name);
548         m_parseLog += QLatin1String("\'.\n");
549     }
550     if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name)) {
551         m_parseLog += QLatin1String("Warning: Missing reference to \'");
552         m_parseLog += QLatin1String(qt_texcoord_attribute_name);
553         m_parseLog += QLatin1String("\'.\n");
554     }
555     if (!m_source.respectsMatrix) {
556         m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Matrix\'.\n");
557     }
558     if (!m_source.respectsOpacity) {
559         m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Opacity\'.\n");
560     }
561
562     for (int i = 0; i < m_sources.size(); ++i) {
563         QVariant v = property(m_sources.at(i).name);
564         setSource(v, i);
565     }
566
567     connectPropertySignals();
568 }
569
570 namespace {
571
572     enum VariableQualifier {
573         AttributeQualifier,
574         UniformQualifier
575     };
576
577     inline bool qt_isalpha(char c)
578     {
579         char ch = c | 0x20;
580         return (ch >= 'a' && ch <= 'z') || c == '_';
581     }
582
583     inline bool qt_isalnum(char c)
584     {
585         return qt_isalpha(c) || (c >= '0' && c <= '9');
586     }
587
588     inline bool qt_isspace(char c)
589     {
590         return c == ' ' || (c >= 0x09 && c <= 0x0d);
591     }
592
593     // Returns -1 if not found, returns index to first character after the name if found.
594     int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
595                                int &typeIndex, int &typeLength,
596                                int &nameIndex, int &nameLength)
597     {
598         enum Identifier {
599             QualifierIdentifier, // Base state
600             PrecisionIdentifier,
601             TypeIdentifier,
602             NameIdentifier
603         };
604         Identifier expected = QualifierIdentifier;
605         bool compilerDirectiveExpected = index == 0;
606
607         while (index < length) {
608             // Skip whitespace.
609             while (qt_isspace(s[index])) {
610                 compilerDirectiveExpected |= s[index] == '\n';
611                 ++index;
612             }
613
614             if (qt_isalpha(s[index])) {
615                 // Read identifier.
616                 int idIndex = index;
617                 ++index;
618                 while (qt_isalnum(s[index]))
619                     ++index;
620                 int idLength = index - idIndex;
621
622                 const int attrLen = sizeof("attribute") - 1;
623                 const int uniLen = sizeof("uniform") - 1;
624                 const int loLen = sizeof("lowp") - 1;
625                 const int medLen = sizeof("mediump") - 1;
626                 const int hiLen = sizeof("highp") - 1;
627
628                 switch (expected) {
629                 case QualifierIdentifier:
630                     if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
631                         decl = AttributeQualifier;
632                         expected = PrecisionIdentifier;
633                     } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
634                         decl = UniformQualifier;
635                         expected = PrecisionIdentifier;
636                     }
637                     break;
638                 case PrecisionIdentifier:
639                     if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
640                             || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
641                             || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
642                     {
643                         expected = TypeIdentifier;
644                         break;
645                     }
646                     // Fall through.
647                 case TypeIdentifier:
648                     typeIndex = idIndex;
649                     typeLength = idLength;
650                     expected = NameIdentifier;
651                     break;
652                 case NameIdentifier:
653                     nameIndex = idIndex;
654                     nameLength = idLength;
655                     return index; // Attribute or uniform declaration found. Return result.
656                 default:
657                     break;
658                 }
659             } else if (s[index] == '#' && compilerDirectiveExpected) {
660                 // Skip compiler directives.
661                 ++index;
662                 while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
663                     ++index;
664             } else if (s[index] == '/' && s[index + 1] == '/') {
665                 // Skip comments.
666                 index += 2;
667                 while (index < length && s[index] != '\n')
668                     ++index;
669             } else if (s[index] == '/' && s[index + 1] == '*') {
670                 // Skip comments.
671                 index += 2;
672                 while (index < length && (s[index] != '*' || s[index + 1] != '/'))
673                     ++index;
674                 if (index < length)
675                     index += 2; // Skip star-slash.
676             } else {
677                 expected = QualifierIdentifier;
678                 ++index;
679             }
680             compilerDirectiveExpected = false;
681         }
682         return -1;
683     }
684 }
685
686 void QQuickShaderEffect::lookThroughShaderCode(const QByteArray &code)
687 {
688     int index = 0;
689     int typeIndex, typeLength, nameIndex, nameLength;
690     const char *s = code.constData();
691     VariableQualifier decl;
692     while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
693                                            nameIndex, nameLength)) != -1)
694     {
695         if (decl == AttributeQualifier) {
696             m_source.attributeNames.append(QByteArray(s + nameIndex, nameLength));
697         } else {
698             Q_ASSERT(decl == UniformQualifier);
699
700             const int matLen = sizeof("qt_Matrix") - 1;
701             const int opLen = sizeof("qt_Opacity") - 1;
702             const int sampLen = sizeof("sampler2D") - 1;
703
704             if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
705                 m_source.respectsMatrix = true;
706             } else if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
707                 m_source.respectsOpacity = true;
708             } else {
709                 QByteArray name(s + nameIndex, nameLength);
710                 m_source.uniformNames.insert(name);
711                 if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0) {
712                     SourceData d;
713                     d.mapper = new QSignalMapper;
714                     d.name = name;
715                     d.sourceObject = 0;
716                     m_sources.append(d);
717                 }
718             }
719         }
720     }
721 }
722
723 void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
724 {
725     m_dirtyGeometry = true;
726     QQuickItem::geometryChanged(newGeometry, oldGeometry);
727 }
728
729 QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
730 {
731     QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
732
733     ensureCompleted();
734
735     // In the case of a bad vertex shader, don't try to create a node...
736     if (m_source.attributeNames.isEmpty()) {
737         if (node)
738             delete node;
739         return 0;
740     }
741
742     if (!node) {
743         node = new QQuickShaderEffectNode;
744         m_programDirty = true;
745         m_dirtyData = true;
746         m_dirtyGeometry = true;
747         connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
748     }
749
750     QQuickShaderEffectMaterial *material = node->shaderMaterial();
751
752     if (m_dirtyMesh) {
753         node->setGeometry(0);
754         m_dirtyMesh = false;
755         m_dirtyGeometry = true;
756     }
757
758     if (m_dirtyGeometry) {
759         node->setFlag(QSGNode::OwnsGeometry, false);
760         QSGGeometry *geometry = node->geometry();
761         QRectF rect(0, 0, width(), height());
762         QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
763
764         geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect);
765         if (!geometry) {
766             QString log = mesh->log();
767             if (!log.isNull()) {
768                 m_log = m_parseLog;
769                 m_log += QLatin1String("*** Mesh ***\n");
770                 m_log += log;
771                 m_status = Error;
772                 emit logChanged();
773                 emit statusChanged();
774             }
775             delete node;
776             return 0;
777         }
778
779         node->setGeometry(geometry);
780         node->setFlag(QSGNode::OwnsGeometry, true);
781
782         m_dirtyGeometry = false;
783     }
784
785     if (m_programDirty) {
786         QQuickShaderEffectProgram s = m_source;
787         if (s.fragmentCode.isEmpty())
788             s.fragmentCode = qt_default_fragment_code;
789         if (s.vertexCode.isEmpty())
790             s.vertexCode = qt_default_vertex_code;
791         s.className = metaObject()->className();
792
793         material->setProgramSource(s);
794         node->markDirty(QSGNode::DirtyMaterial);
795         m_programDirty = false;
796     }
797
798     // Update blending
799     if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
800         material->setFlag(QSGMaterial::Blending, m_blending);
801         node->markDirty(QSGNode::DirtyMaterial);
802     }
803
804     if (int(material->cullMode()) != int(m_cullMode)) {
805         material->setCullMode(QQuickShaderEffectMaterial::CullMode(m_cullMode));
806         node->markDirty(QSGNode::DirtyMaterial);
807     }
808
809     if (m_dirtyData) {
810         QVector<QPair<QByteArray, QVariant> > values;
811         QVector<QPair<QByteArray, QSGTextureProvider *> > textures;
812         const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = material->textureProviders();
813
814         for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
815              it != m_source.uniformNames.end(); ++it) {
816             values.append(qMakePair(*it, property(*it)));
817         }
818         for (int i = 0; i < oldTextures.size(); ++i) {
819             QSGTextureProvider *t = oldTextures.at(i).second;
820             if (t)
821                 disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
822         }
823         for (int i = 0; i < m_sources.size(); ++i) {
824             const SourceData &source = m_sources.at(i);
825             QSGTextureProvider *t = source.sourceObject ? source.sourceObject->textureProvider() : 0;
826             textures.append(qMakePair(source.name, t));
827             if (t)
828                 connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
829         }
830         material->setUniforms(values);
831         material->setTextureProviders(textures);
832         node->markDirty(QSGNode::DirtyMaterial);
833         m_dirtyData = false;
834     }
835
836     return node;
837 }
838
839 void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
840 {
841     if (change == QQuickItem::ItemSceneChange) {
842         // See comment in QQuickShaderEffect::setSource().
843         for (int i = 0; i < m_sources.size(); ++i) {
844             QQuickItemPrivate *d = QQuickItemPrivate::get(m_sources.at(i).sourceObject);
845             if (!d->parentItem && value.canvas != d->canvas) {
846                 QQuickItemPrivate::InitializationState initState;
847                 initState.clear();
848                 d->initCanvas(&initState, value.canvas);
849             }
850         }
851     }
852     QQuickItem::itemChange(change, value);
853 }
854
855
856 QT_END_NAMESPACE