Remove "All rights reserved" line from license headers.
[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 QtDeclarative 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     QQuickItem *item = qobject_cast<QQuickItem *>(obj);
417     if (!item || !item->isTextureProvider()) {
418         qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]",
419                  source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className());
420         return;
421     }
422
423     source.sourceObject = item;
424
425     if (item) {
426         QQuickItemPrivate *d = QQuickItemPrivate::get(item);
427         // 'item' needs a canvas to get a scene graph node. It usually gets one through its
428         // parent, but if the source item is "inline" rather than a reference -- i.e.
429         // "property variant source: Image { }" instead of "property variant source: foo" -- it
430         // will not get a parent. In those cases, 'item' should get the canvas from 'this'.
431         if (!d->parentItem && canvas() && !d->canvas) {
432             QQuickItemPrivate::InitializationState initState;
433             initState.clear();
434             d->initCanvas(&initState, canvas());
435         }
436     }
437 }
438
439 void QQuickShaderEffect::disconnectPropertySignals()
440 {
441     disconnect(this, 0, this, SLOT(updateData()));
442     for (int i = 0; i < m_sources.size(); ++i) {
443         SourceData &source = m_sources[i];
444         disconnect(this, 0, source.mapper, 0);
445         disconnect(source.mapper, 0, this, 0);
446     }
447 }
448
449 void QQuickShaderEffect::connectPropertySignals()
450 {
451     QSet<QByteArray>::const_iterator it;
452     for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
453         int pi = metaObject()->indexOfProperty(it->constData());
454         if (pi >= 0) {
455             QMetaProperty mp = metaObject()->property(pi);
456             if (!mp.hasNotifySignal())
457                 qWarning("QQuickShaderEffect: property '%s' does not have notification method!", it->constData());
458             QByteArray signalName("2");
459             signalName.append(mp.notifySignal().signature());
460             connect(this, signalName, this, SLOT(updateData()));
461         } else {
462             // If the source is set via a dynamic property, like the layer is, then we need this check
463             // to disable the warning.
464             if (property(it->constData()).isValid())
465                 continue;
466             qWarning("QQuickShaderEffect: '%s' does not have a matching property!", it->constData());
467         }
468     }
469     for (int i = 0; i < m_sources.size(); ++i) {
470         SourceData &source = m_sources[i];
471         int pi = metaObject()->indexOfProperty(source.name.constData());
472         if (pi >= 0) {
473             QMetaProperty mp = metaObject()->property(pi);
474             QByteArray signalName("2");
475             signalName.append(mp.notifySignal().signature());
476             connect(this, signalName, source.mapper, SLOT(map()));
477             source.mapper->setMapping(this, i);
478             connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
479         } else {
480             // If the source is set via a dynamic property, like the layer is, then we need this check
481             // to disable the warning.
482             if (property(source.name.constData()).isValid())
483                 continue;
484             qWarning("QQuickShaderEffect: '%s' does not have a matching source!", source.name.constData());
485         }
486     }
487 }
488
489
490 void QQuickShaderEffect::ensureCompleted()
491 {
492     if (!m_complete) {
493         reset();
494         updateProperties();
495         m_complete = true;
496     }
497 }
498
499
500 void QQuickShaderEffect::reset()
501 {
502     disconnectPropertySignals();
503
504     m_source.attributeNames.clear();
505     m_source.uniformNames.clear();
506     m_source.respectsOpacity = false;
507     m_source.respectsMatrix = false;
508     m_source.className = metaObject()->className();
509
510     for (int i = 0; i < m_sources.size(); ++i) {
511         const SourceData &source = m_sources.at(i);
512         delete source.mapper;
513     }
514     m_sources.clear();
515     m_log.clear();
516     m_parseLog.clear();
517     m_programDirty = true;
518     m_dirtyMesh = true;
519 }
520
521 void QQuickShaderEffect::updateProperties()
522 {
523     if (m_source.vertexCode.isEmpty()) {
524         m_source.attributeNames.append(QByteArray(qt_position_attribute_name));
525         m_source.attributeNames.append(QByteArray(qt_texcoord_attribute_name));
526         m_source.respectsMatrix = true;
527     } else {
528         lookThroughShaderCode(m_source.vertexCode);
529     }
530     if (m_source.fragmentCode.isEmpty()) {
531         m_source.respectsOpacity = true;
532         QByteArray name("source");
533         m_source.uniformNames.insert(name);
534         SourceData d;
535         d.mapper = new QSignalMapper;
536         d.name = name;
537         d.sourceObject = 0;
538         m_sources.append(d);
539     } else {
540         lookThroughShaderCode(m_source.fragmentCode);
541     }
542
543     if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) {
544         m_parseLog += QLatin1String("Warning: Missing reference to \'");
545         m_parseLog += QLatin1String(qt_position_attribute_name);
546         m_parseLog += QLatin1String("\'.\n");
547     }
548     if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name)) {
549         m_parseLog += QLatin1String("Warning: Missing reference to \'");
550         m_parseLog += QLatin1String(qt_texcoord_attribute_name);
551         m_parseLog += QLatin1String("\'.\n");
552     }
553     if (!m_source.respectsMatrix) {
554         m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Matrix\'.\n");
555     }
556     if (!m_source.respectsOpacity) {
557         m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Opacity\'.\n");
558     }
559
560     for (int i = 0; i < m_sources.size(); ++i) {
561         QVariant v = property(m_sources.at(i).name);
562         setSource(v, i);
563     }
564
565     connectPropertySignals();
566 }
567
568 namespace {
569
570     enum VariableQualifier {
571         AttributeQualifier,
572         UniformQualifier
573     };
574
575     inline bool qt_isalpha(char c)
576     {
577         char ch = c | 0x20;
578         return (ch >= 'a' && ch <= 'z') || c == '_';
579     }
580
581     inline bool qt_isalnum(char c)
582     {
583         return qt_isalpha(c) || (c >= '0' && c <= '9');
584     }
585
586     inline bool qt_isspace(char c)
587     {
588         return c == ' ' || (c >= 0x09 && c <= 0x0d);
589     }
590
591     // Returns -1 if not found, returns index to first character after the name if found.
592     int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
593                                int &typeIndex, int &typeLength,
594                                int &nameIndex, int &nameLength)
595     {
596         enum Identifier {
597             QualifierIdentifier, // Base state
598             PrecisionIdentifier,
599             TypeIdentifier,
600             NameIdentifier
601         };
602         Identifier expected = QualifierIdentifier;
603         bool compilerDirectiveExpected = index == 0;
604
605         while (index < length) {
606             // Skip whitespace.
607             while (qt_isspace(s[index])) {
608                 compilerDirectiveExpected |= s[index] == '\n';
609                 ++index;
610             }
611
612             if (qt_isalpha(s[index])) {
613                 // Read identifier.
614                 int idIndex = index;
615                 ++index;
616                 while (qt_isalnum(s[index]))
617                     ++index;
618                 int idLength = index - idIndex;
619
620                 const int attrLen = sizeof("attribute") - 1;
621                 const int uniLen = sizeof("uniform") - 1;
622                 const int loLen = sizeof("lowp") - 1;
623                 const int medLen = sizeof("mediump") - 1;
624                 const int hiLen = sizeof("highp") - 1;
625
626                 switch (expected) {
627                 case QualifierIdentifier:
628                     if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
629                         decl = AttributeQualifier;
630                         expected = PrecisionIdentifier;
631                     } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
632                         decl = UniformQualifier;
633                         expected = PrecisionIdentifier;
634                     }
635                     break;
636                 case PrecisionIdentifier:
637                     if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
638                             || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
639                             || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
640                     {
641                         expected = TypeIdentifier;
642                         break;
643                     }
644                     // Fall through.
645                 case TypeIdentifier:
646                     typeIndex = idIndex;
647                     typeLength = idLength;
648                     expected = NameIdentifier;
649                     break;
650                 case NameIdentifier:
651                     nameIndex = idIndex;
652                     nameLength = idLength;
653                     return index; // Attribute or uniform declaration found. Return result.
654                 default:
655                     break;
656                 }
657             } else if (s[index] == '#' && compilerDirectiveExpected) {
658                 // Skip compiler directives.
659                 ++index;
660                 while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
661                     ++index;
662             } else if (s[index] == '/' && s[index + 1] == '/') {
663                 // Skip comments.
664                 index += 2;
665                 while (index < length && s[index] != '\n')
666                     ++index;
667             } else if (s[index] == '/' && s[index + 1] == '*') {
668                 // Skip comments.
669                 index += 2;
670                 while (index < length && (s[index] != '*' || s[index + 1] != '/'))
671                     ++index;
672                 if (index < length)
673                     index += 2; // Skip star-slash.
674             } else {
675                 expected = QualifierIdentifier;
676                 ++index;
677             }
678             compilerDirectiveExpected = false;
679         }
680         return -1;
681     }
682 }
683
684 void QQuickShaderEffect::lookThroughShaderCode(const QByteArray &code)
685 {
686     int index = 0;
687     int typeIndex, typeLength, nameIndex, nameLength;
688     const char *s = code.constData();
689     VariableQualifier decl;
690     while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
691                                            nameIndex, nameLength)) != -1)
692     {
693         if (decl == AttributeQualifier) {
694             m_source.attributeNames.append(QByteArray(s + nameIndex, nameLength));
695         } else {
696             Q_ASSERT(decl == UniformQualifier);
697
698             const int matLen = sizeof("qt_Matrix") - 1;
699             const int opLen = sizeof("qt_Opacity") - 1;
700             const int sampLen = sizeof("sampler2D") - 1;
701
702             if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
703                 m_source.respectsMatrix = true;
704             } else if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
705                 m_source.respectsOpacity = true;
706             } else {
707                 QByteArray name(s + nameIndex, nameLength);
708                 m_source.uniformNames.insert(name);
709                 if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0) {
710                     SourceData d;
711                     d.mapper = new QSignalMapper;
712                     d.name = name;
713                     d.sourceObject = 0;
714                     m_sources.append(d);
715                 }
716             }
717         }
718     }
719 }
720
721 void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
722 {
723     m_dirtyGeometry = true;
724     QQuickItem::geometryChanged(newGeometry, oldGeometry);
725 }
726
727 QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
728 {
729     QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
730
731     ensureCompleted();
732
733     // In the case of a bad vertex shader, don't try to create a node...
734     if (m_source.attributeNames.isEmpty()) {
735         if (node)
736             delete node;
737         return 0;
738     }
739
740     if (!node) {
741         node = new QQuickShaderEffectNode;
742         m_programDirty = true;
743         m_dirtyData = true;
744         m_dirtyGeometry = true;
745         connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
746     }
747
748     QQuickShaderEffectMaterial *material = node->shaderMaterial();
749
750     if (m_dirtyMesh) {
751         node->setGeometry(0);
752         m_dirtyMesh = false;
753         m_dirtyGeometry = true;
754     }
755
756     if (m_dirtyGeometry) {
757         node->setFlag(QSGNode::OwnsGeometry, false);
758         QSGGeometry *geometry = node->geometry();
759         QRectF rect(0, 0, width(), height());
760         QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
761
762         geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect);
763         if (!geometry) {
764             QString log = mesh->log();
765             if (!log.isNull()) {
766                 m_log = m_parseLog;
767                 m_log += QLatin1String("*** Mesh ***\n");
768                 m_log += log;
769                 m_status = Error;
770                 emit logChanged();
771                 emit statusChanged();
772             }
773             delete node;
774             return 0;
775         }
776
777         node->setGeometry(geometry);
778         node->setFlag(QSGNode::OwnsGeometry, true);
779
780         m_dirtyGeometry = false;
781     }
782
783     if (m_programDirty) {
784         QQuickShaderEffectProgram s = m_source;
785         if (s.fragmentCode.isEmpty())
786             s.fragmentCode = qt_default_fragment_code;
787         if (s.vertexCode.isEmpty())
788             s.vertexCode = qt_default_vertex_code;
789         s.className = metaObject()->className();
790
791         material->setProgramSource(s);
792         node->markDirty(QSGNode::DirtyMaterial);
793         m_programDirty = false;
794     }
795
796     // Update blending
797     if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
798         material->setFlag(QSGMaterial::Blending, m_blending);
799         node->markDirty(QSGNode::DirtyMaterial);
800     }
801
802     if (int(material->cullMode()) != int(m_cullMode)) {
803         material->setCullMode(QQuickShaderEffectMaterial::CullMode(m_cullMode));
804         node->markDirty(QSGNode::DirtyMaterial);
805     }
806
807     if (m_dirtyData) {
808         QVector<QPair<QByteArray, QVariant> > values;
809         QVector<QPair<QByteArray, QSGTextureProvider *> > textures;
810         const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = material->textureProviders();
811
812         for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
813              it != m_source.uniformNames.end(); ++it) {
814             values.append(qMakePair(*it, property(*it)));
815         }
816         for (int i = 0; i < oldTextures.size(); ++i) {
817             QSGTextureProvider *t = oldTextures.at(i).second;
818             if (t)
819                 disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
820         }
821         for (int i = 0; i < m_sources.size(); ++i) {
822             const SourceData &source = m_sources.at(i);
823             QSGTextureProvider *t = source.sourceObject ? source.sourceObject->textureProvider() : 0;
824             textures.append(qMakePair(source.name, t));
825             if (t)
826                 connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
827         }
828         material->setUniforms(values);
829         material->setTextureProviders(textures);
830         node->markDirty(QSGNode::DirtyMaterial);
831         m_dirtyData = false;
832     }
833
834     return node;
835 }
836
837 void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
838 {
839     if (change == QQuickItem::ItemSceneChange) {
840         // See comment in QQuickShaderEffect::setSource().
841         for (int i = 0; i < m_sources.size(); ++i) {
842             QQuickItemPrivate *d = QQuickItemPrivate::get(m_sources.at(i).sourceObject);
843             if (!d->parentItem && value.canvas != d->canvas) {
844                 QQuickItemPrivate::InitializationState initState;
845                 initState.clear();
846                 d->initCanvas(&initState, value.canvas);
847             }
848         }
849     }
850     QQuickItem::itemChange(change, value);
851 }
852
853
854 QT_END_NAMESPACE