diff options
Diffstat (limited to 'src/imports')
32 files changed, 5686 insertions, 0 deletions
diff --git a/src/imports/folderlistmodel/folderlistmodel.pro b/src/imports/folderlistmodel/folderlistmodel.pro new file mode 100644 index 00000000..0f639797 --- /dev/null +++ b/src/imports/folderlistmodel/folderlistmodel.pro @@ -0,0 +1,26 @@ +TARGET = qmlfolderlistmodelplugin +TARGETPATH = Qt/labs/folderlistmodel +include(../qimportbase.pri) + +QT += declarative script + +SOURCES += qdeclarativefolderlistmodel.cpp plugin.cpp +HEADERS += qdeclarativefolderlistmodel.h + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +symbian:{ + TARGET.UID3 = 0x20021320 + + isEmpty(DESTDIR):importFiles.files = qmlfolderlistmodelplugin$${QT_LIBINFIX}.dll qmldir + else:importFiles.files = $$DESTDIR/qmlfolderlistmodelplugin$${QT_LIBINFIX}.dll qmldir + importFiles.path = $$QT_IMPORTS_BASE_DIR/$$TARGETPATH + + DEPLOYMENT += importFiles +} + +INSTALLS += target qmldir diff --git a/src/imports/folderlistmodel/plugin.cpp b/src/imports/folderlistmodel/plugin.cpp new file mode 100644 index 00000000..61054a1e --- /dev/null +++ b/src/imports/folderlistmodel/plugin.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtDeclarative/qdeclarativeextensionplugin.h> +#include <QtDeclarative/qdeclarative.h> + +#include "qdeclarativefolderlistmodel.h" + +QT_BEGIN_NAMESPACE + +//![class decl] +class QmlFolderListModelPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.folderlistmodel")); +#ifndef QT_NO_DIRMODEL + qmlRegisterType<QDeclarativeFolderListModel>(uri,1,0,"FolderListModel"); +#endif + } +}; +//![class decl] + +QT_END_NAMESPACE + +#include "plugin.moc" + +//![plugin export decl] +Q_EXPORT_PLUGIN2(qmlfolderlistmodelplugin, QT_PREPEND_NAMESPACE(QmlFolderListModelPlugin)); +//![plugin export decl] + diff --git a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp new file mode 100644 index 00000000..efc0b8be --- /dev/null +++ b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp @@ -0,0 +1,485 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//![code] +#include "qdeclarativefolderlistmodel.h" +#include <QDirModel> +#include <QDebug> +#include <qdeclarativecontext.h> + +#ifndef QT_NO_DIRMODEL + +QT_BEGIN_NAMESPACE + +class QDeclarativeFolderListModelPrivate +{ +public: + QDeclarativeFolderListModelPrivate() + : sortField(QDeclarativeFolderListModel::Name), sortReversed(false), count(0) { + nameFilters << QLatin1String("*"); + } + + void updateSorting() { + QDir::SortFlags flags = 0; + switch(sortField) { + case QDeclarativeFolderListModel::Unsorted: + flags |= QDir::Unsorted; + break; + case QDeclarativeFolderListModel::Name: + flags |= QDir::Name; + break; + case QDeclarativeFolderListModel::Time: + flags |= QDir::Time; + break; + case QDeclarativeFolderListModel::Size: + flags |= QDir::Size; + break; + case QDeclarativeFolderListModel::Type: + flags |= QDir::Type; + break; + } + + if (sortReversed) + flags |= QDir::Reversed; + + model.setSorting(flags); + } + + QDirModel model; + QUrl folder; + QStringList nameFilters; + QModelIndex folderIndex; + QDeclarativeFolderListModel::SortField sortField; + bool sortReversed; + int count; +}; + +/*! + \qmlclass FolderListModel QDeclarativeFolderListModel + \ingroup qml-working-with-data + \brief The FolderListModel provides a model of the contents of a file system folder. + + FolderListModel provides access to information about the contents of a folder + in the local file system, exposing a list of files to views and other data components. + + \note This type is made available by importing the \c Qt.labs.folderlistmodel module. + \e{Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions.} + + \bold{import Qt.labs.folderlistmodel 1.0} + + The \l folder property specifies the folder to access. Information about the + files and directories in the folder is supplied via the model's interface. + Components access names and paths via the following roles: + + \list + \o fileName + \o filePath + \endlist + + Additionally a file entry can be differentiated from a folder entry via the + isFolder() method. + + \section1 Filtering + + Various properties can be set to filter the number of files and directories + exposed by the model. + + The \l nameFilters property can be set to contain a list of wildcard filters + that are applied to names of files and directories, causing only those that + match the filters to be exposed. + + Directories can be included or excluded using the \l showDirs property, and + navigation directories can also be excluded by setting the \l showDotAndDotDot + property to false. + + It is sometimes useful to limit the files and directories exposed to those + that the user can access. The \l showOnlyReadable property can be set to + enable this feature. + + \section1 Example Usage + + The following example shows a FolderListModel being used to provide a list + of QML files in a \l ListView: + + \snippet doc/src/snippets/declarative/folderlistmodel.qml 0 + + \section1 Path Separators + + Qt uses "/" as a universal directory separator in the same way that "/" is + used as a path separator in URLs. If you always use "/" as a directory + separator, Qt will translate your paths to conform to the underlying + operating system. + + \sa {QML Data Models} +*/ + +QDeclarativeFolderListModel::QDeclarativeFolderListModel(QObject *parent) + : QAbstractListModel(parent) +{ + QHash<int, QByteArray> roles; + roles[FileNameRole] = "fileName"; + roles[FilePathRole] = "filePath"; + setRoleNames(roles); + + d = new QDeclarativeFolderListModelPrivate; + d->model.setFilter(QDir::AllDirs | QDir::Files | QDir::Drives | QDir::NoDotAndDotDot); + connect(&d->model, SIGNAL(rowsInserted(const QModelIndex&,int,int)) + , this, SLOT(inserted(const QModelIndex&,int,int))); + connect(&d->model, SIGNAL(rowsRemoved(const QModelIndex&,int,int)) + , this, SLOT(removed(const QModelIndex&,int,int))); + connect(&d->model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)) + , this, SLOT(handleDataChanged(const QModelIndex&,const QModelIndex&))); + connect(&d->model, SIGNAL(modelReset()), this, SLOT(refresh())); + connect(&d->model, SIGNAL(layoutChanged()), this, SLOT(refresh())); +} + +QDeclarativeFolderListModel::~QDeclarativeFolderListModel() +{ + delete d; +} + +QVariant QDeclarativeFolderListModel::data(const QModelIndex &index, int role) const +{ + QVariant rv; + QModelIndex modelIndex = d->model.index(index.row(), 0, d->folderIndex); + if (modelIndex.isValid()) { + if (role == FileNameRole) + rv = d->model.data(modelIndex, QDirModel::FileNameRole).toString(); + else if (role == FilePathRole) + rv = QUrl::fromLocalFile(d->model.data(modelIndex, QDirModel::FilePathRole).toString()); + } + return rv; +} + +/*! + \qmlproperty int FolderListModel::count + + Returns the number of items in the current folder that match the + filter criteria. +*/ +int QDeclarativeFolderListModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return d->count; +} + +/*! + \qmlproperty string FolderListModel::folder + + The \a folder property holds a URL for the folder that the model is + currently providing. + + The value is a URL expressed as a string, and must be a \c file: or \c qrc: + URL, or a relative URL. + + By default, the value is an invalid URL. +*/ +QUrl QDeclarativeFolderListModel::folder() const +{ + return d->folder; +} + +void QDeclarativeFolderListModel::setFolder(const QUrl &folder) +{ + if (folder == d->folder) + return; + QModelIndex index = d->model.index(folder.toLocalFile()); + if ((index.isValid() && d->model.isDir(index)) || folder.toLocalFile().isEmpty()) { + + d->folder = folder; + QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); + emit folderChanged(); + } +} + +/*! + \qmlproperty url FolderListModel::parentFolder + + Returns the URL of the parent of of the current \l folder. +*/ +QUrl QDeclarativeFolderListModel::parentFolder() const +{ + QString localFile = d->folder.toLocalFile(); + if (!localFile.isEmpty()) { + QDir dir(localFile); +#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WIN) + if (dir.isRoot()) + dir.setPath(""); + else +#endif + dir.cdUp(); + localFile = dir.path(); + } else { + int pos = d->folder.path().lastIndexOf(QLatin1Char('/')); + if (pos == -1) + return QUrl(); + localFile = d->folder.path().left(pos); + } + return QUrl::fromLocalFile(localFile); +} + +/*! + \qmlproperty list<string> FolderListModel::nameFilters + + The \a nameFilters property contains a list of file name filters. + The filters may include the ? and * wildcards. + + The example below filters on PNG and JPEG files: + + \qml + FolderListModel { + nameFilters: [ "*.png", "*.jpg" ] + } + \endqml + + \note Directories are not excluded by filters. +*/ +QStringList QDeclarativeFolderListModel::nameFilters() const +{ + return d->nameFilters; +} + +void QDeclarativeFolderListModel::setNameFilters(const QStringList &filters) +{ + d->nameFilters = filters; + d->model.setNameFilters(d->nameFilters); +} + +void QDeclarativeFolderListModel::classBegin() +{ +} + +void QDeclarativeFolderListModel::componentComplete() +{ + if (!d->folder.isValid() || d->folder.toLocalFile().isEmpty() || !QDir().exists(d->folder.toLocalFile())) + setFolder(QUrl(QLatin1String("file://")+QDir::currentPath())); + + if (!d->folderIndex.isValid()) + QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); +} + +/*! + \qmlproperty enumeration FolderListModel::sortField + + The \a sortField property contains field to use for sorting. sortField + may be one of: + \list + \o Unsorted - no sorting is applied. The order is system default. + \o Name - sort by filename + \o Time - sort by time modified + \o Size - sort by file size + \o Type - sort by file type (extension) + \endlist + + \sa sortReversed +*/ +QDeclarativeFolderListModel::SortField QDeclarativeFolderListModel::sortField() const +{ + return d->sortField; +} + +void QDeclarativeFolderListModel::setSortField(SortField field) +{ + if (field != d->sortField) { + d->sortField = field; + d->updateSorting(); + } +} + +/*! + \qmlproperty bool FolderListModel::sortReversed + + If set to true, reverses the sort order. The default is false. + + \sa sortField +*/ +bool QDeclarativeFolderListModel::sortReversed() const +{ + return d->sortReversed; +} + +void QDeclarativeFolderListModel::setSortReversed(bool rev) +{ + if (rev != d->sortReversed) { + d->sortReversed = rev; + d->updateSorting(); + } +} + +/*! + \qmlmethod bool FolderListModel::isFolder(int index) + + Returns true if the entry \a index is a folder; otherwise + returns false. +*/ +bool QDeclarativeFolderListModel::isFolder(int index) const +{ + if (index != -1) { + QModelIndex idx = d->model.index(index, 0, d->folderIndex); + if (idx.isValid()) + return d->model.isDir(idx); + } + return false; +} + +void QDeclarativeFolderListModel::refresh() +{ + d->folderIndex = QModelIndex(); + if (d->count) { + emit beginRemoveRows(QModelIndex(), 0, d->count-1); + d->count = 0; + emit endRemoveRows(); + } + d->folderIndex = d->model.index(d->folder.toLocalFile()); + int newcount = d->model.rowCount(d->folderIndex); + if (newcount) { + emit beginInsertRows(QModelIndex(), 0, newcount-1); + d->count = newcount; + emit endInsertRows(); + } +} + +void QDeclarativeFolderListModel::inserted(const QModelIndex &index, int start, int end) +{ + if (index == d->folderIndex) { + emit beginInsertRows(QModelIndex(), start, end); + d->count = d->model.rowCount(d->folderIndex); + emit endInsertRows(); + } +} + +void QDeclarativeFolderListModel::removed(const QModelIndex &index, int start, int end) +{ + if (index == d->folderIndex) { + emit beginRemoveRows(QModelIndex(), start, end); + d->count = d->model.rowCount(d->folderIndex); + emit endRemoveRows(); + } +} + +void QDeclarativeFolderListModel::handleDataChanged(const QModelIndex &start, const QModelIndex &end) +{ + if (start.parent() == d->folderIndex) + emit dataChanged(index(start.row(),0), index(end.row(),0)); +} + +/*! + \qmlproperty bool FolderListModel::showDirs + + If true, directories are included in the model; otherwise only files + are included. + + By default, this property is true. + + Note that the nameFilters are not applied to directories. + + \sa showDotAndDotDot +*/ +bool QDeclarativeFolderListModel::showDirs() const +{ + return d->model.filter() & QDir::AllDirs; +} + +void QDeclarativeFolderListModel::setShowDirs(bool on) +{ + if (!(d->model.filter() & QDir::AllDirs) == !on) + return; + if (on) + d->model.setFilter(d->model.filter() | QDir::AllDirs | QDir::Drives); + else + d->model.setFilter(d->model.filter() & ~(QDir::AllDirs | QDir::Drives)); +} + +/*! + \qmlproperty bool FolderListModel::showDotAndDotDot + + If true, the "." and ".." directories are included in the model; otherwise + they are excluded. + + By default, this property is false. + + \sa showDirs +*/ +bool QDeclarativeFolderListModel::showDotAndDotDot() const +{ + return !(d->model.filter() & QDir::NoDotAndDotDot); +} + +void QDeclarativeFolderListModel::setShowDotAndDotDot(bool on) +{ + if (!(d->model.filter() & QDir::NoDotAndDotDot) == on) + return; + if (on) + d->model.setFilter(d->model.filter() & ~QDir::NoDotAndDotDot); + else + d->model.setFilter(d->model.filter() | QDir::NoDotAndDotDot); +} + +/*! + \qmlproperty bool FolderListModel::showOnlyReadable + + If true, only readable files and directories are shown; otherwise all files + and directories are shown. + + By default, this property is false. + + \sa showDirs +*/ +bool QDeclarativeFolderListModel::showOnlyReadable() const +{ + return d->model.filter() & QDir::Readable; +} + +void QDeclarativeFolderListModel::setShowOnlyReadable(bool on) +{ + if (!(d->model.filter() & QDir::Readable) == !on) + return; + if (on) + d->model.setFilter(d->model.filter() | QDir::Readable); + else + d->model.setFilter(d->model.filter() & ~QDir::Readable); +} + +//![code] +QT_END_NAMESPACE + +#endif // QT_NO_DIRMODEL diff --git a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h new file mode 100644 index 00000000..5ec2416b --- /dev/null +++ b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFOLDERLISTMODEL_H +#define QDECLARATIVEFOLDERLISTMODEL_H + +#include <qdeclarative.h> +#include <QStringList> +#include <QUrl> +#include <QAbstractListModel> + +#ifndef QT_NO_DIRMODEL + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeContext; +class QModelIndex; + +class QDeclarativeFolderListModelPrivate; + +//![class begin] +class QDeclarativeFolderListModel : public QAbstractListModel, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) +//![class begin] + +//![class props] + Q_PROPERTY(QUrl folder READ folder WRITE setFolder NOTIFY folderChanged) + Q_PROPERTY(QUrl parentFolder READ parentFolder NOTIFY folderChanged) + Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters) + Q_PROPERTY(SortField sortField READ sortField WRITE setSortField) + Q_PROPERTY(bool sortReversed READ sortReversed WRITE setSortReversed) + Q_PROPERTY(bool showDirs READ showDirs WRITE setShowDirs) + Q_PROPERTY(bool showDotAndDotDot READ showDotAndDotDot WRITE setShowDotAndDotDot) + Q_PROPERTY(bool showOnlyReadable READ showOnlyReadable WRITE setShowOnlyReadable) + Q_PROPERTY(int count READ count) +//![class props] + +//![abslistmodel] +public: + QDeclarativeFolderListModel(QObject *parent = 0); + ~QDeclarativeFolderListModel(); + + enum Roles { FileNameRole = Qt::UserRole+1, FilePathRole = Qt::UserRole+2 }; + + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; +//![abslistmodel] + +//![count] + int count() const { return rowCount(QModelIndex()); } +//![count] + +//![prop funcs] + QUrl folder() const; + void setFolder(const QUrl &folder); + + QUrl parentFolder() const; + + QStringList nameFilters() const; + void setNameFilters(const QStringList &filters); + + enum SortField { Unsorted, Name, Time, Size, Type }; + SortField sortField() const; + void setSortField(SortField field); + Q_ENUMS(SortField) + + bool sortReversed() const; + void setSortReversed(bool rev); + + bool showDirs() const; + void setShowDirs(bool); + bool showDotAndDotDot() const; + void setShowDotAndDotDot(bool); + bool showOnlyReadable() const; + void setShowOnlyReadable(bool); +//![prop funcs] + +//![isfolder] + Q_INVOKABLE bool isFolder(int index) const; +//![isfolder] + +//![parserstatus] + virtual void classBegin(); + virtual void componentComplete(); +//![parserstatus] + +//![notifier] +Q_SIGNALS: + void folderChanged(); +//![notifier] + +//![class end] +private Q_SLOTS: + void refresh(); + void inserted(const QModelIndex &index, int start, int end); + void removed(const QModelIndex &index, int start, int end); + void handleDataChanged(const QModelIndex &start, const QModelIndex &end); + +private: + Q_DISABLE_COPY(QDeclarativeFolderListModel) + QDeclarativeFolderListModelPrivate *d; +}; +//![class end] + +QT_END_NAMESPACE + +//![qml decl] +QML_DECLARE_TYPE(QDeclarativeFolderListModel) +//![qml decl] + +QT_END_HEADER + +#endif // QT_NO_DIRMODEL + +#endif // QDECLARATIVEFOLDERLISTMODEL_H diff --git a/src/imports/folderlistmodel/qmldir b/src/imports/folderlistmodel/qmldir new file mode 100644 index 00000000..6e115bbc --- /dev/null +++ b/src/imports/folderlistmodel/qmldir @@ -0,0 +1 @@ +plugin qmlfolderlistmodelplugin diff --git a/src/imports/gestures/gestures.pro b/src/imports/gestures/gestures.pro new file mode 100644 index 00000000..2768cc9c --- /dev/null +++ b/src/imports/gestures/gestures.pro @@ -0,0 +1,26 @@ +TARGET = qmlgesturesplugin +TARGETPATH = Qt/labs/gestures +include(../qimportbase.pri) + +QT += declarative + +SOURCES += qdeclarativegesturearea.cpp plugin.cpp +HEADERS += qdeclarativegesturearea_p.h + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +symbian:{ + TARGET.UID3 = 0x2002131F + + isEmpty(DESTDIR):importFiles.files = qmlgesturesplugin$${QT_LIBINFIX}.dll qmldir + else:importFiles.files = $$DESTDIR/qmlgesturesplugin$${QT_LIBINFIX}.dll qmldir + importFiles.path = $$QT_IMPORTS_BASE_DIR/$$TARGETPATH + + DEPLOYMENT += importFiles +} + +INSTALLS += target qmldir diff --git a/src/imports/gestures/plugin.cpp b/src/imports/gestures/plugin.cpp new file mode 100644 index 00000000..15c9d22e --- /dev/null +++ b/src/imports/gestures/plugin.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtDeclarative/qdeclarativeextensionplugin.h> +#include <QtDeclarative/qdeclarative.h> + +#include "qdeclarativegesturearea_p.h" + +QT_BEGIN_NAMESPACE + +class GestureAreaQmlPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.gestures")); +#ifndef QT_NO_GESTURES + qmlRegisterCustomType<QDeclarativeGestureArea>(uri,1,0, "GestureArea", new QDeclarativeGestureAreaParser); + + qmlRegisterUncreatableType<QGesture>(uri, 1, 0, "Gesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QPanGesture>(uri, 1, 0, "PanGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QTapGesture>(uri, 1, 0, "TapGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QTapAndHoldGesture>(uri, 1, 0, "TapAndHoldGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QPinchGesture>(uri, 1, 0, "PinchGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QSwipeGesture>(uri, 1, 0, "SwipeGesture", QLatin1String("Do not create objects of this type.")); +#endif + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" + +Q_EXPORT_PLUGIN2(qmlgesturesplugin, QT_PREPEND_NAMESPACE(GestureAreaQmlPlugin)); diff --git a/src/imports/gestures/qdeclarativegesturearea.cpp b/src/imports/gestures/qdeclarativegesturearea.cpp new file mode 100644 index 00000000..afd92b06 --- /dev/null +++ b/src/imports/gestures/qdeclarativegesturearea.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegesturearea_p.h" + +#include <qdeclarativeexpression.h> +#include <qdeclarativecontext.h> +#include <qdeclarativeinfo.h> + +#include <private/qdeclarativeproperty_p.h> +#include <private/qdeclarativeitem_p.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qstringlist.h> + +#include <QtGui/qevent.h> + +#include <private/qobject_p.h> + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +class QDeclarativeGestureAreaPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeGestureArea) +public: + QDeclarativeGestureAreaPrivate() : componentcomplete(false), gesture(0) {} + + typedef QMap<Qt::GestureType,QDeclarativeExpression*> Bindings; + Bindings bindings; + + bool componentcomplete; + + QByteArray data; + + QGesture *gesture; + + bool gestureEvent(QGestureEvent *event); +}; + +/*! + \qmlclass GestureArea QDeclarativeGestureArea + \ingroup qml-basic-interaction-elements + + \brief The GestureArea item enables simple gesture handling. + \inherits Item + + A GestureArea is like a MouseArea, but it has signals for gesture events. + + \warning Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions. + + \warning GestureArea is an experimental element whose development has + been discontinued. PinchArea is available in QtQuick 1.1 and handles + two finger gesture input. + + \note This element is only functional on devices with touch input. + + \qml + import Qt.labs.gestures 1.0 + + GestureArea { + anchors.fill: parent + // onPan: ... gesture.acceleration ... + // onPinch: ... gesture.rotationAngle ... + // onSwipe: ... + // onTapAndHold: ... + // onTap: ... + // onGesture: ... + } + \endqml + + Each signal has a \e gesture parameter that has the + properties of the gesture. + + \table + \header \o Signal \o Type \o Property \o Description + \row \o onTap \o point \o position \o the position of the tap + \row \o onTapAndHold \o point \o position \o the position of the tap + \row \o onPan \o real \o acceleration \o the acceleration of the pan + \row \o onPan \o point \o delta \o the offset from the previous input position to the current input + \row \o onPan \o point \o offset \o the total offset from the first input position to the current input position + \row \o onPan \o point \o lastOffset \o the previous value of offset + \row \o onPinch \o point \o centerPoint \o the midpoint between the two input points + \row \o onPinch \o point \o lastCenterPoint \o the previous value of centerPoint + \row \o onPinch \o point \o startCenterPoint \o the first value of centerPoint + \row \o onPinch \o real \o rotationAngle \o the angle covered by the gesture motion + \row \o onPinch \o real \o lastRotationAngle \o the previous value of rotationAngle + \row \o onPinch \o real \o totalRotationAngle \o the complete angle covered by the gesture + \row \o onPinch \o real \o scaleFactor \o the change in distance between the two input points + \row \o onPinch \o real \o lastScaleFactor \o the previous value of scaleFactor + \row \o onPinch \o real \o totalScaleFactor \o the complete scale factor of the gesture + \row \o onSwipe \o real \o swipeAngle \o the angle of the swipe + \endtable + + Custom gestures, handled by onGesture, will have custom properties. + + GestureArea is an invisible item: it is never painted. + + \sa MouseArea +*/ + +/*! + \internal + \class QDeclarativeGestureArea + \brief The QDeclarativeGestureArea class provides simple gesture handling. + +*/ +QDeclarativeGestureArea::QDeclarativeGestureArea(QDeclarativeItem *parent) : + QDeclarativeItem(*(new QDeclarativeGestureAreaPrivate), parent) +{ + setAcceptedMouseButtons(Qt::LeftButton); + setAcceptTouchEvents(true); +} + +QDeclarativeGestureArea::~QDeclarativeGestureArea() +{ +} + +QByteArray +QDeclarativeGestureAreaParser::compile(const QList<QDeclarativeCustomParserProperty> &props) +{ + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for(int ii = 0; ii < props.count(); ++ii) + { + QString propName = QString::fromUtf8(props.at(ii).name()); + Qt::GestureType type; + + if (propName == QLatin1String("onTap")) { + type = Qt::TapGesture; + } else if (propName == QLatin1String("onTapAndHold")) { + type = Qt::TapAndHoldGesture; + } else if (propName == QLatin1String("onPan")) { + type = Qt::PanGesture; + } else if (propName == QLatin1String("onPinch")) { + type = Qt::PinchGesture; + } else if (propName == QLatin1String("onSwipe")) { + type = Qt::SwipeGesture; + } else if (propName == QLatin1String("onGesture")) { + type = Qt::CustomGesture; + } else { + error(props.at(ii), QDeclarativeGestureArea::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); + } + + QList<QVariant> values = props.at(ii).assignedValues(); + + for (int i = 0; i < values.count(); ++i) { + const QVariant &value = values.at(i); + + if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { + error(props.at(ii), QDeclarativeGestureArea::tr("GestureArea: nested objects not allowed")); + return QByteArray(); + } else if (value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) { + error(props.at(ii), QDeclarativeGestureArea::tr("GestureArea: syntax error")); + return QByteArray(); + } else { + QDeclarativeParser::Variant v = qvariant_cast<QDeclarativeParser::Variant>(value); + if (v.isScript()) { + ds << propName; + ds << int(type); + ds << v.asScript(); + } else { + error(props.at(ii), QDeclarativeGestureArea::tr("GestureArea: script expected")); + return QByteArray(); + } + } + } + } + + return rv; +} + +void QDeclarativeGestureAreaParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarativeGestureArea *ga = static_cast<QDeclarativeGestureArea*>(object); + ga->d_func()->data = data; +} + + +void QDeclarativeGestureArea::connectSignals() +{ + Q_D(QDeclarativeGestureArea); + if (!d->componentcomplete) + return; + + QDataStream ds(d->data); + while (!ds.atEnd()) { + QString propName; + ds >> propName; + int gesturetype; + ds >> gesturetype; + QString script; + ds >> script; + QDeclarativeExpression *exp = new QDeclarativeExpression(qmlContext(this), this, script); + d->bindings.insert(Qt::GestureType(gesturetype),exp); + grabGesture(Qt::GestureType(gesturetype)); + } +} + +void QDeclarativeGestureArea::componentComplete() +{ + QDeclarativeItem::componentComplete(); + Q_D(QDeclarativeGestureArea); + d->componentcomplete=true; + connectSignals(); +} + +QGesture *QDeclarativeGestureArea::gesture() const +{ + Q_D(const QDeclarativeGestureArea); + return d->gesture; +} + +bool QDeclarativeGestureArea::sceneEvent(QEvent *event) +{ + Q_D(QDeclarativeGestureArea); + if (event->type() == QEvent::Gesture) + return d->gestureEvent(static_cast<QGestureEvent*>(event)); + return QDeclarativeItem::sceneEvent(event); +} + +bool QDeclarativeGestureAreaPrivate::gestureEvent(QGestureEvent *event) +{ + bool accept = true; + for (Bindings::Iterator it = bindings.begin(); it != bindings.end(); ++it) { + if ((gesture = event->gesture(it.key()))) { + QDeclarativeExpression *expr = it.value(); + expr->evaluate(); + if (expr->hasError()) + qmlInfo(q_func()) << expr->error(); + event->setAccepted(true); // XXX only if value returns true? + } + } + return accept; +} + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES diff --git a/src/imports/gestures/qdeclarativegesturearea_p.h b/src/imports/gestures/qdeclarativegesturearea_p.h new file mode 100644 index 00000000..8171b376 --- /dev/null +++ b/src/imports/gestures/qdeclarativegesturearea_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGESTUREAREA_H +#define QDECLARATIVEGESTUREAREA_H + +#include <qdeclarativeitem.h> +#include <qdeclarativescriptstring.h> +#include <private/qdeclarativecustomparser_p.h> + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtGui/qgesture.h> + +#ifndef QT_NO_GESTURES + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeBoundSignal; +class QDeclarativeContext; +class QDeclarativeGestureAreaPrivate; +class QDeclarativeGestureArea : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QGesture *gesture READ gesture) + +public: + QDeclarativeGestureArea(QDeclarativeItem *parent=0); + ~QDeclarativeGestureArea(); + + QGesture *gesture() const; + +protected: + bool sceneEvent(QEvent *event); + +private: + void connectSignals(); + void componentComplete(); + friend class QDeclarativeGestureAreaParser; + + Q_DISABLE_COPY(QDeclarativeGestureArea) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeGestureArea) +}; + +class QDeclarativeGestureAreaParser : public QDeclarativeCustomParser +{ +public: + virtual QByteArray compile(const QList<QDeclarativeCustomParserProperty> &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGestureArea) + +QT_END_HEADER + +#endif // QT_NO_GESTURES + +#endif diff --git a/src/imports/gestures/qmldir b/src/imports/gestures/qmldir new file mode 100644 index 00000000..2a31920a --- /dev/null +++ b/src/imports/gestures/qmldir @@ -0,0 +1 @@ +plugin qmlgesturesplugin diff --git a/src/imports/imports.pro b/src/imports/imports.pro new file mode 100644 index 00000000..d0e24b0b --- /dev/null +++ b/src/imports/imports.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS += folderlistmodel particles gestures +contains(QT_CONFIG, opengl):!contains(QT_CONFIG, opengles1): SUBDIRS += shaders + diff --git a/src/imports/particles/particles.cpp b/src/imports/particles/particles.cpp new file mode 100644 index 00000000..533ec08d --- /dev/null +++ b/src/imports/particles/particles.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtDeclarative/qdeclarativeextensionplugin.h> +#include <QtDeclarative/qdeclarative.h> + +#include "qdeclarativeparticles_p.h" + +QT_BEGIN_NAMESPACE + +class QParticlesQmlModule : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.particles")); + qmlRegisterType<QDeclarativeParticleMotion>(uri,1,0,"ParticleMotion"); + qmlRegisterType<QDeclarativeParticleMotionGravity>(uri,1,0,"ParticleMotionGravity"); + qmlRegisterType<QDeclarativeParticleMotionLinear>(uri,1,0,"ParticleMotionLinear"); + qmlRegisterType<QDeclarativeParticleMotionWander>(uri,1,0,"ParticleMotionWander"); + qmlRegisterType<QDeclarativeParticles>(uri,1,0,"Particles"); + } +}; + +QT_END_NAMESPACE + +#include "particles.moc" + +Q_EXPORT_PLUGIN2(qmlparticlesplugin, QT_PREPEND_NAMESPACE(QParticlesQmlModule)); + diff --git a/src/imports/particles/particles.pro b/src/imports/particles/particles.pro new file mode 100644 index 00000000..894d164c --- /dev/null +++ b/src/imports/particles/particles.pro @@ -0,0 +1,30 @@ +TARGET = qmlparticlesplugin +TARGETPATH = Qt/labs/particles +include(../qimportbase.pri) + +QT += declarative + +SOURCES += \ + qdeclarativeparticles.cpp \ + particles.cpp + +HEADERS += \ + qdeclarativeparticles_p.h + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +symbian:{ + TARGET.UID3 = 0x2002131E + + isEmpty(DESTDIR):importFiles.files = qmlparticlesplugin$${QT_LIBINFIX}.dll qmldir + else:importFiles.files = $$DESTDIR/qmlparticlesplugin$${QT_LIBINFIX}.dll qmldir + importFiles.path = $$QT_IMPORTS_BASE_DIR/$$TARGETPATH + + DEPLOYMENT += importFiles +} + +INSTALLS += target qmldir diff --git a/src/imports/particles/qdeclarativeparticles.cpp b/src/imports/particles/qdeclarativeparticles.cpp new file mode 100644 index 00000000..b2b304a7 --- /dev/null +++ b/src/imports/particles/qdeclarativeparticles.cpp @@ -0,0 +1,1296 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeparticles_p.h" + +#include <qdeclarativeinfo.h> +#include <private/qdeclarativeitem_p.h> + +#include <private/qdeclarativepixmapcache_p.h> +#include <QtCore/QAbstractAnimation> + +#include <QPainter> +#include <QtGui/qdrawutil.h> +#include <QVarLengthArray> + +#include <stdlib.h> +#include <math.h> + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#define M_PI_2 (M_PI / 2.) +#endif +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif + +QT_BEGIN_NAMESPACE +#define PI_SQR 9.8696044 +// parabolic approximation +inline qreal fastSin(qreal theta) +{ + const qreal b = 4 / M_PI; + const qreal c = -4 / PI_SQR; + + qreal y = b * theta + c * theta * qAbs(theta); + return y; +} + +inline qreal fastCos(qreal theta) +{ + theta += M_PI_2; + if (theta > M_PI) + theta -= 2 * M_PI; + + return fastSin(theta); +} + +class QDeclarativeParticle +{ +public: + QDeclarativeParticle(int time) : lifeSpan(1000), fadeOutAge(800) + , opacity(0), birthTime(time), x_velocity(0), y_velocity(0) + , state(FadeIn), data(0) + { + } + + int lifeSpan; + int fadeOutAge; + qreal x; + qreal y; + qreal opacity; + int birthTime; + qreal x_velocity; + qreal y_velocity; + enum State { FadeIn, Solid, FadeOut }; + State state; + void *data; +}; + +//--------------------------------------------------------------------------- + +/*! + \class QDeclarativeParticleMotion + \ingroup group_effects + \brief The QDeclarativeParticleMotion class is the base class for particle motion. + \internal + + This class causes the particles to remain static. +*/ + +/*! + Constructs a QDeclarativeParticleMotion with parent object \a parent. +*/ +QDeclarativeParticleMotion::QDeclarativeParticleMotion(QObject *parent) : + QObject(parent) +{ +} + +/*! + Move the \a particle to its new position. \a interval is the number of + milliseconds elapsed since it was last moved. +*/ +void QDeclarativeParticleMotion::advance(QDeclarativeParticle &particle, int interval) +{ + Q_UNUSED(particle); + Q_UNUSED(interval); +} + +/*! + The \a particle has just been created. Some motion strategies require + additional state information. This can be allocated by this function. +*/ +void QDeclarativeParticleMotion::created(QDeclarativeParticle &particle) +{ + Q_UNUSED(particle); +} + +/*! + The \a particle is about to be destroyed. Any additional memory + that has been allocated for the particle should be freed. +*/ +void QDeclarativeParticleMotion::destroy(QDeclarativeParticle &particle) +{ + Q_UNUSED(particle); +} + +/*! + \qmlclass ParticleMotionLinear QDeclarativeParticleMotionLinear + \ingroup qml-particle-elements + \since 4.7 + \brief The ParticleMotionLinear object moves particles linearly. + + \sa Particles + + This is the default motion, and moves the particles according to the + properties specified in the Particles element. + + It has no further properties. +*/ +void QDeclarativeParticleMotionLinear::advance(QDeclarativeParticle &p, int interval) +{ + p.x += interval * p.x_velocity; + p.y += interval * p.y_velocity; +} + +/*! + \qmlclass ParticleMotionGravity QDeclarativeParticleMotionGravity + \ingroup qml-particle-elements + \since 4.7 + \brief The ParticleMotionGravity object moves particles towards a point. + + This motion attracts the particles to the specified point with the specified acceleration. + To mimic earth gravity, set yattractor to -6360000 and acceleration to 9.8. + + The defaults are all 0, not earth gravity, and so no motion will occur without setting + at least the acceleration property. + + + \sa Particles +*/ + +/*! + \qmlproperty real ParticleMotionGravity::xattractor + \qmlproperty real ParticleMotionGravity::yattractor + These properties hold the x and y coordinates of the point attracting the particles. +*/ + +/*! + \qmlproperty real ParticleMotionGravity::acceleration + This property holds the acceleration to apply to the particles. +*/ + +/*! + \property QDeclarativeParticleMotionGravity::xattractor + \brief the x coordinate of the point attracting the particles. +*/ + +/*! + \property QDeclarativeParticleMotionGravity::yattractor + \brief the y coordinate of the point attracting the particles. +*/ + +/*! + \property QDeclarativeParticleMotionGravity::acceleration + \brief the acceleration to apply to the particles. +*/ + +void QDeclarativeParticleMotionGravity::setXAttractor(qreal x) +{ + if (qFuzzyCompare(x, _xAttr)) + return; + _xAttr = x; + emit xattractorChanged(); +} + +void QDeclarativeParticleMotionGravity::setYAttractor(qreal y) +{ + if (qFuzzyCompare(y, _yAttr)) + return; + _yAttr = y; + emit yattractorChanged(); +} + +void QDeclarativeParticleMotionGravity::setAcceleration(qreal accel) +{ + qreal scaledAccel = accel/1000000.0; + if (qFuzzyCompare(scaledAccel, _accel)) + return; + _accel = scaledAccel; + emit accelerationChanged(); +} + +void QDeclarativeParticleMotionGravity::advance(QDeclarativeParticle &p, int interval) +{ + qreal xdiff = _xAttr - p.x; + qreal ydiff = _yAttr - p.y; + qreal absXdiff = qAbs(xdiff); + qreal absYdiff = qAbs(ydiff); + + qreal xcomp = xdiff / (absXdiff + absYdiff); + qreal ycomp = ydiff / (absXdiff + absYdiff); + + p.x_velocity += xcomp * _accel * interval; + p.y_velocity += ycomp * _accel * interval; + + p.x += interval * p.x_velocity; + p.y += interval * p.y_velocity; +} + +/*! + \qmlclass ParticleMotionWander QDeclarativeParticleMotionWander + \ingroup qml-particle-elements + \since 4.7 + \brief The ParticleMotionWander object moves particles in a somewhat random fashion. + + The particles will continue roughly in the original direction, however will randomly + drift to each side. + + The code below produces an effect similar to falling snow. + + \qml +Rectangle { + width: 240 + height: 320 + color: "black" + + Particles { + y: 0 + width: parent.width + height: 30 + source: "star.png" + lifeSpan: 5000 + count: 50 + angle: 70 + angleDeviation: 36 + velocity: 30 + velocityDeviation: 10 + ParticleMotionWander { + xvariance: 30 + pace: 100 + } + } +} + \endqml + + \sa Particles +*/ + +/*! + \qmlproperty real ParticleMotionWander::xvariance + \qmlproperty real ParticleMotionWander::yvariance + + These properties set the amount to wander in the x and y directions. +*/ + +/*! + \qmlproperty real ParticleMotionWander::pace + This property holds how quickly the paricles will move from side to side. +*/ + +void QDeclarativeParticleMotionWander::advance(QDeclarativeParticle &p, int interval) +{ + if (!particles) + particles = qobject_cast<QDeclarativeParticles*>(parent()); + if (particles) { + Data *d = (Data*)p.data; + if (_xvariance != 0.) { + qreal xdiff = p.x_velocity - d->x_targetV; + if ((xdiff > d->x_peak && d->x_var > 0.0) || (xdiff < -d->x_peak && d->x_var < 0.0)) { + d->x_var = -d->x_var; + d->x_peak = _xvariance + _xvariance * qreal(qrand()) / RAND_MAX; + } + p.x_velocity += d->x_var * interval; + } + p.x += interval * p.x_velocity; + + if (_yvariance != 0.) { + qreal ydiff = p.y_velocity - d->y_targetV; + if ((ydiff > d->y_peak && d->y_var > 0.0) || (ydiff < -d->y_peak && d->y_var < 0.0)) { + d->y_var = -d->y_var; + d->y_peak = _yvariance + _yvariance * qreal(qrand()) / RAND_MAX; + } + p.y_velocity += d->y_var * interval; + } + p.y += interval * p.y_velocity; + } +} + +void QDeclarativeParticleMotionWander::created(QDeclarativeParticle &p) +{ + if (!p.data) { + Data *d = new Data; + p.data = (void*)d; + d->x_targetV = p.x_velocity; + d->y_targetV = p.y_velocity; + d->x_peak = _xvariance; + d->y_peak = _yvariance; + d->x_var = _pace * qreal(qrand()) / RAND_MAX / 1000.0; + d->y_var = _pace * qreal(qrand()) / RAND_MAX / 1000.0; + } +} + +void QDeclarativeParticleMotionWander::destroy(QDeclarativeParticle &p) +{ + if (p.data) + delete (Data*)p.data; +} + +void QDeclarativeParticleMotionWander::setXVariance(qreal var) +{ + qreal scaledVar = var / 1000.0; + if (qFuzzyCompare(scaledVar, _xvariance)) + return; + _xvariance = scaledVar; + emit xvarianceChanged(); +} + +void QDeclarativeParticleMotionWander::setYVariance(qreal var) +{ + qreal scaledVar = var / 1000.0; + if (qFuzzyCompare(scaledVar, _yvariance)) + return; + _yvariance = scaledVar; + emit yvarianceChanged(); +} + +void QDeclarativeParticleMotionWander::setPace(qreal pace) +{ + qreal scaledPace = pace / 1000.0; + if (qFuzzyCompare(scaledPace, _pace)) + return; + _pace = scaledPace; + emit paceChanged(); +} + +//--------------------------------------------------------------------------- +class QDeclarativeParticlesPainter : public QDeclarativeItem +{ +public: + QDeclarativeParticlesPainter(QDeclarativeParticlesPrivate *p, QDeclarativeItem* parent) + : QDeclarativeItem(parent), d(p) + { + setFlag(QGraphicsItem::ItemHasNoContents, false); + maxX = minX = maxY = minY = 0; + } + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + + void updateSize(); + + qreal maxX; + qreal minX; + qreal maxY; + qreal minY; + QDeclarativeParticlesPrivate* d; +}; + +//an animation that just gives a tick +template<class T, void (T::*method)(int)> +class TickAnimationProxy : public QAbstractAnimation +{ +public: + TickAnimationProxy(T *p, QObject *parent = 0) : QAbstractAnimation(parent), m_p(p) {} + virtual int duration() const { return -1; } +protected: + virtual void updateCurrentTime(int msec) { (m_p->*method)(msec); } + +private: + T *m_p; +}; + +//--------------------------------------------------------------------------- +class QDeclarativeParticlesPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeParticles) +public: + QDeclarativeParticlesPrivate() + : count(1), emissionRate(-1), emissionVariance(0.5), lifeSpan(1000) + , lifeSpanDev(1000), fadeInDur(200), fadeOutDur(300) + , angle(0), angleDev(0), velocity(0), velocityDev(0), emissionCarry(0.) + , addParticleTime(0), addParticleCount(0), lastAdvTime(0) + , motion(0), clock(this) + { + } + + ~QDeclarativeParticlesPrivate() + { + } + + void init() + { + Q_Q(QDeclarativeParticles); + paintItem = new QDeclarativeParticlesPainter(this, q); + } + + void tick(int time); + void createParticle(int time); + void updateOpacity(QDeclarativeParticle &p, int age); + + QUrl url; + QDeclarativePixmap image; + int count; + int emissionRate; + qreal emissionVariance; + int lifeSpan; + int lifeSpanDev; + int fadeInDur; + int fadeOutDur; + qreal angle; + qreal angleDev; + qreal velocity; + qreal velocityDev; + qreal emissionCarry; + int addParticleTime; + int addParticleCount; + int lastAdvTime; + QDeclarativeParticleMotion *motion; + QDeclarativeParticlesPainter *paintItem; + + + QList<QPair<int, int> > bursts;//countLeft, emissionRate pairs + QList<QDeclarativeParticle> particles; + TickAnimationProxy<QDeclarativeParticlesPrivate, &QDeclarativeParticlesPrivate::tick> clock; + +}; + +void QDeclarativeParticlesPrivate::tick(int time) +{ + Q_Q(QDeclarativeParticles); + if (!motion) + motion = new QDeclarativeParticleMotionLinear(q); + + int oldCount = particles.count(); + int removed = 0; + int interval = time - lastAdvTime; + for (int i = 0; i < particles.count(); ) { + QDeclarativeParticle &particle = particles[i]; + int age = time - particle.birthTime; + if (age >= particle.lifeSpan) { + QDeclarativeParticle part = particles.takeAt(i); + motion->destroy(part); + ++removed; + } else { + updateOpacity(particle, age); + motion->advance(particle, interval); + ++i; + } + } + + if(emissionRate == -1)//Otherwise leave emission to the emission rate + while(removed-- && ((count == -1) || particles.count() < count)) + createParticle(time); + + if (!addParticleTime) + addParticleTime = time; + + //Possibly emit new particles + if (((count == -1) || particles.count() < count) && emissionRate + && !(count==-1 && emissionRate==-1)) { + int emissionCount = -1; + if (emissionRate != -1){ + qreal variance = 1.; + if (emissionVariance > 0.){ + variance += (qreal(qrand())/RAND_MAX) * emissionVariance * (qrand()%2?-1.:1.); + } + qreal emission = emissionRate * (qreal(interval)/1000.); + emission = emission * variance + emissionCarry; + double tmpDbl; + emissionCarry = modf(emission, &tmpDbl); + emissionCount = (int)tmpDbl; + emissionCount = qMax(0,emissionCount); + } + while(((count == -1) || particles.count() < count) && + (emissionRate==-1 || emissionCount--)) + createParticle(time); + } + + //Deal with emissions from requested bursts + for(int i=0; i<bursts.size(); i++){ + int emission = 0; + if(bursts[i].second == -1){ + emission = bursts[i].first; + }else{ + qreal variance = 1.; + if (emissionVariance > 0.){ + variance += (qreal(qrand())/RAND_MAX) * emissionVariance * (qrand()%2?-1.:1.); + } + qreal workingEmission = bursts[i].second * (qreal(interval)/1000.); + workingEmission *= variance; + emission = (int)workingEmission; + emission = qMax(emission, 0); + } + emission = qMin(emission, bursts[i].first); + bursts[i].first -= emission; + while(emission--) + createParticle(time); + } + for(int i=bursts.size()-1; i>=0; i--) + if(bursts[i].first <= 0) + bursts.removeAt(i); + + lastAdvTime = time; + paintItem->updateSize(); + paintItem->update(); + if (!(oldCount || particles.count()) && (!count || !emissionRate) && bursts.isEmpty()) { + lastAdvTime = 0; + clock.stop(); + } +} + +void QDeclarativeParticlesPrivate::createParticle(int time) +{ + Q_Q(QDeclarativeParticles); + QDeclarativeParticle p(time); + p.x = q->x() + q->width() * qreal(qrand()) / RAND_MAX - image.width()/2.0; + p.y = q->y() + q->height() * qreal(qrand()) / RAND_MAX - image.height()/2.0; + p.lifeSpan = lifeSpan; + if (lifeSpanDev) + p.lifeSpan += int(lifeSpanDev/2 - lifeSpanDev * qreal(qrand()) / RAND_MAX); + p.fadeOutAge = p.lifeSpan - fadeOutDur; + if (fadeInDur == 0.) { + p.state= QDeclarativeParticle::Solid; + p.opacity = 1.0; + } + qreal a = angle; + if (angleDev) + a += angleDev/2 - angleDev * qreal(qrand()) / RAND_MAX; + if (a > M_PI) + a = a - 2 * M_PI; + qreal v = velocity; + if (velocityDev) + v += velocityDev/2 - velocityDev * qreal(qrand()) / RAND_MAX; + p.x_velocity = v * fastCos(a); + p.y_velocity = v * fastSin(a); + particles.append(p); + motion->created(particles.last()); +} + +void QDeclarativeParticlesPrivate::updateOpacity(QDeclarativeParticle &p, int age) +{ + switch (p.state) { + case QDeclarativeParticle::FadeIn: + if (age <= fadeInDur) { + p.opacity = qreal(age) / fadeInDur; + break; + } else { + p.opacity = 1.0; + p.state = QDeclarativeParticle::Solid; + // Fall through + } + case QDeclarativeParticle::Solid: + if (age <= p.fadeOutAge) { + break; + } else { + p.state = QDeclarativeParticle::FadeOut; + // Fall through + } + case QDeclarativeParticle::FadeOut: + p.opacity = qreal(p.lifeSpan - age) / fadeOutDur; + break; + } +} + +/*! + \qmlclass Particles QDeclarativeParticles + \ingroup qml-particle-elements + \since 4.7 + \brief The Particles object generates and moves particles. + \inherits Item + + Particles are available in the \bold{Qt.labs.particles 1.0} module. + \e {Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions.} + + This element provides preliminary support for particles in QML, + and may be heavily changed or removed in later versions. + + The particles created by this object cannot be dealt with + directly, they can only be controlled through the parameters of + the Particles object. The particles are all the same pixmap, + specified by the user. + + The particles are painted relative to the parent of the Particles + object. Moving the Particles object will not move the particles + already emitted. + + The below example creates two differently behaving particle + sources. The top one has particles falling from the top like + snow, the lower one has particles expelled up like a fountain. + + \qml +import QtQuick 1.0 +import Qt.labs.particles 1.0 + +Rectangle { + width: 240 + height: 320 + color: "black" + Particles { + y: 0 + width: parent.width + height: 30 + source: "star.png" + lifeSpan: 5000 + count: 50 + angle: 70 + angleDeviation: 36 + velocity: 30 + velocityDeviation: 10 + ParticleMotionWander { + xvariance: 30 + pace: 100 + } + } + Particles { + y: 300 + x: 120 + width: 1 + height: 1 + source: "star.png" + lifeSpan: 5000 + count: 200 + angle: 270 + angleDeviation: 45 + velocity: 50 + velocityDeviation: 30 + ParticleMotionGravity { + yattractor: 1000 + xattractor: 0 + acceleration: 25 + } + } +} + \endqml + \image particles.gif +*/ + +QDeclarativeParticles::QDeclarativeParticles(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativeParticlesPrivate), parent) +{ + Q_D(QDeclarativeParticles); + d->init(); +} + +QDeclarativeParticles::~QDeclarativeParticles() +{ +} + +/*! + \qmlproperty string Particles::source + This property holds the URL of the particle image. +*/ + +/*! + \property QDeclarativeParticles::source + \brief the URL of the particle image. +*/ +QUrl QDeclarativeParticles::source() const +{ + Q_D(const QDeclarativeParticles); + return d->url; +} + +void QDeclarativeParticles::imageLoaded() +{ + Q_D(QDeclarativeParticles); + if (d->image.isError()) + qmlInfo(this) << d->image.error(); + d->paintItem->updateSize(); + d->paintItem->update(); +} + +void QDeclarativeParticles::setSource(const QUrl &name) +{ + Q_D(QDeclarativeParticles); + + if ((d->url.isEmpty() == name.isEmpty()) && name == d->url) + return; + + if (name.isEmpty()) { + d->url = name; + d->image.clear(this); + d->paintItem->updateSize(); + d->paintItem->update(); + } else { + d->url = name; + Q_ASSERT(!name.isRelative()); + d->image.load(qmlEngine(this), d->url); + if (d->image.isLoading()) { + d->image.connectFinished(this, SLOT(imageLoaded())); + } else { + if (d->image.isError()) + qmlInfo(this) << d->image.error(); + //### unify with imageLoaded + d->paintItem->updateSize(); + d->paintItem->update(); + } + } + emit sourceChanged(); +} + +/*! + \qmlproperty int Particles::count + This property holds the maximum number of particles + + The particles element emits particles until it has count active + particles. When this number is reached, new particles are not emitted until + some of the current particles reach the end of their lifespan. + + If count is -1 then there is no maximum number of active particles, and + particles will be constantly emitted at the rate specified by emissionRate. + + The default value for count is 1. + + If both count and emissionRate are set to -1, nothing will be emitted. + +*/ + +/*! + \property QDeclarativeParticles::count + \brief the maximum number of particles +*/ +int QDeclarativeParticles::count() const +{ + Q_D(const QDeclarativeParticles); + return d->count; +} + +void QDeclarativeParticles::setCount(int cnt) +{ + Q_D(QDeclarativeParticles); + if (cnt == d->count) + return; + + int oldCount = d->count; + d->count = cnt; + d->addParticleTime = 0; + d->addParticleCount = d->particles.count(); + if (!oldCount && d->clock.state() != QAbstractAnimation::Running && d->count && d->emissionRate) { + d->clock.start(); + } + d->paintItem->updateSize(); + d->paintItem->update(); + emit countChanged(); +} + + +/*! + \qmlproperty int Particles::emissionRate + This property holds the target number of particles to emit every second. + + The particles element will emit up to emissionRate particles every + second. Fewer particles may be emitted per second if the maximum number of + particles has been reached. + + If emissionRate is set to -1 there is no limit to the number of + particles emitted per second, and particles will be instantly emitted to + reach the maximum number of particles specified by count. + + The default value for emissionRate is -1. + + If both count and emissionRate are set to -1, nothing will be emitted. +*/ + +/*! + \property QDeclarativeParticles::emissionRate + \brief the emission rate of particles +*/ +int QDeclarativeParticles::emissionRate() const +{ + Q_D(const QDeclarativeParticles); + return d->emissionRate; +} +void QDeclarativeParticles::setEmissionRate(int er) +{ + Q_D(QDeclarativeParticles); + if(er == d->emissionRate) + return; + d->emissionRate = er; + if (d->clock.state() != QAbstractAnimation::Running && d->count && d->emissionRate) { + d->clock.start(); + } + emit emissionRateChanged(); +} + +/*! + \qmlproperty real Particles::emissionVariance + This property holds how inconsistent the rate of particle emissions are. + It is a number between 0 (no variance) and 1 (some variance). + + The expected number of particles emitted per second is emissionRate. If + emissionVariance is 0 then particles will be emitted consistently throughout + each second to reach that number. If emissionVariance is greater than 0 the + rate of particle emission will vary randomly throughout the second, with the + consequence that the actual number of particles emitted in one second will + vary randomly as well. + + emissionVariance is the maximum deviation from emitting + emissionRate particles per second. An emissionVariance of 0 means you should + get exactly emissionRate particles emitted per second, + and an emissionVariance of 1 means you will get between zero and two times + emissionRate particles per second, but you should get emissionRate particles + per second on average. + + Note that even with an emissionVariance of 0 there may be some variance due + to performance and hardware constraints. + + The default value of emissionVariance is 0.5 +*/ + +/*! + \property QDeclarativeParticles::emissionVariance + \brief how much the particle emission amounts vary per tick +*/ + +qreal QDeclarativeParticles::emissionVariance() const +{ + Q_D(const QDeclarativeParticles); + return d->emissionVariance; +} + +void QDeclarativeParticles::setEmissionVariance(qreal ev) +{ + Q_D(QDeclarativeParticles); + if(d->emissionVariance == ev) + return; + d->emissionVariance = ev; + emit emissionVarianceChanged(); +} + +/*! + \qmlproperty int Particles::lifeSpan + \qmlproperty int Particles::lifeSpanDeviation + + These properties describe the life span of each particle. + + The default lifespan for a particle is 1000ms. + + lifeSpanDeviation randomly varies the lifeSpan up to the specified variation. For + example, the following creates particles whose lifeSpan will vary + from 150ms to 250ms: + + \qml +Particles { + source: "star.png" + lifeSpan: 200 + lifeSpanDeviation: 100 +} + \endqml +*/ + +/*! + \property QDeclarativeParticles::lifeSpan + \brief the life span of each particle. + + Default value is 1000ms. + + \sa QDeclarativeParticles::lifeSpanDeviation +*/ +int QDeclarativeParticles::lifeSpan() const +{ + Q_D(const QDeclarativeParticles); + return d->lifeSpan; +} + +void QDeclarativeParticles::setLifeSpan(int ls) +{ + Q_D(QDeclarativeParticles); + if(d->lifeSpan == ls) + return; + d->lifeSpan = ls; + emit lifeSpanChanged(); +} + +/*! + \property QDeclarativeParticles::lifeSpanDeviation + \brief the maximum possible deviation from the set lifeSpan. + + Randomly varies the lifeSpan up to the specified variation. For + example, the following creates particles whose lifeSpan will vary + from 150ms to 250ms: + +\qml +Particles { + source: "star.png" + lifeSpan: 200 + lifeSpanDeviation: 100 +} +\endqml + + \sa QDeclarativeParticles::lifeSpan +*/ +int QDeclarativeParticles::lifeSpanDeviation() const +{ + Q_D(const QDeclarativeParticles); + return d->lifeSpanDev; +} + +void QDeclarativeParticles::setLifeSpanDeviation(int dev) +{ + Q_D(QDeclarativeParticles); + if(d->lifeSpanDev == dev) + return; + d->lifeSpanDev = dev; + emit lifeSpanDeviationChanged(); +} + +/*! + \qmlproperty int Particles::fadeInDuration + \qmlproperty int Particles::fadeOutDuration + These properties hold the time taken to fade the particles in and out. + + By default fade in is 200ms and fade out is 300ms. +*/ + +/*! + \property QDeclarativeParticles::fadeInDuration + \brief the time taken to fade in the particles. + + Default value is 200ms. +*/ +int QDeclarativeParticles::fadeInDuration() const +{ + Q_D(const QDeclarativeParticles); + return d->fadeInDur; +} + +void QDeclarativeParticles::setFadeInDuration(int dur) +{ + Q_D(QDeclarativeParticles); + if (dur < 0.0 || dur == d->fadeInDur) + return; + d->fadeInDur = dur; + emit fadeInDurationChanged(); +} + +/*! + \property QDeclarativeParticles::fadeOutDuration + \brief the time taken to fade out the particles. + + Default value is 300ms. +*/ +int QDeclarativeParticles::fadeOutDuration() const +{ + Q_D(const QDeclarativeParticles); + return d->fadeOutDur; +} + +void QDeclarativeParticles::setFadeOutDuration(int dur) +{ + Q_D(QDeclarativeParticles); + if (dur < 0.0 || d->fadeOutDur == dur) + return; + d->fadeOutDur = dur; + emit fadeOutDurationChanged(); +} + +/*! + \qmlproperty real Particles::angle + \qmlproperty real Particles::angleDeviation + + These properties control particle direction. + + angleDeviation randomly varies the direction up to the specified variation. For + example, the following creates particles whose initial direction will + vary from 15 degrees to 105 degrees: + + \qml +Particles { + source: "star.png" + angle: 60 + angleDeviation: 90 +} + \endqml +*/ + +/*! + \property QDeclarativeParticles::angle + \brief the initial angle of direction. + + \sa QDeclarativeParticles::angleDeviation +*/ +qreal QDeclarativeParticles::angle() const +{ + Q_D(const QDeclarativeParticles); + return d->angle * 180.0 / M_PI; +} + +void QDeclarativeParticles::setAngle(qreal angle) +{ + Q_D(QDeclarativeParticles); + qreal radAngle = angle * M_PI / 180.0; + if(radAngle == d->angle) + return; + d->angle = radAngle; + emit angleChanged(); +} + +/*! + \property QDeclarativeParticles::angleDeviation + \brief the maximum possible deviation from the set angle. + + Randomly varies the direction up to the specified variation. For + example, the following creates particles whose initial direction will + vary from 15 degrees to 105 degrees: + +\qml +Particles { + source: "star.png" + angle: 60 + angleDeviation: 90 +} +\endqml + + \sa QDeclarativeParticles::angle +*/ +qreal QDeclarativeParticles::angleDeviation() const +{ + Q_D(const QDeclarativeParticles); + return d->angleDev * 180.0 / M_PI; +} + +void QDeclarativeParticles::setAngleDeviation(qreal dev) +{ + Q_D(QDeclarativeParticles); + qreal radDev = dev * M_PI / 180.0; + if(radDev == d->angleDev) + return; + d->angleDev = radDev; + emit angleDeviationChanged(); +} + +/*! + \qmlproperty real Particles::velocity + \qmlproperty real Particles::velocityDeviation + + These properties control the velocity of the particles. + + velocityDeviation randomly varies the velocity up to the specified variation. For + example, the following creates particles whose initial velocity will + vary from 40 to 60. + + \qml +Particles { + source: "star.png" + velocity: 50 + velocityDeviation: 20 +} + \endqml +*/ + +/*! + \property QDeclarativeParticles::velocity + \brief the initial velocity of the particles. + + \sa QDeclarativeParticles::velocityDeviation +*/ +qreal QDeclarativeParticles::velocity() const +{ + Q_D(const QDeclarativeParticles); + return d->velocity * 1000.0; +} + +void QDeclarativeParticles::setVelocity(qreal velocity) +{ + Q_D(QDeclarativeParticles); + qreal realVel = velocity / 1000.0; + if(realVel == d->velocity) + return; + d->velocity = realVel; + emit velocityChanged(); +} + +/*! + \property QDeclarativeParticles::velocityDeviation + \brief the maximum possible deviation from the set velocity. + + Randomly varies the velocity up to the specified variation. For + example, the following creates particles whose initial velocity will + vary from 40 to 60. + +\qml +Particles { + source: "star.png" + velocity: 50 + velocityDeviation: 20 +} +\endqml + + \sa QDeclarativeParticles::velocity +*/ +qreal QDeclarativeParticles::velocityDeviation() const +{ + Q_D(const QDeclarativeParticles); + return d->velocityDev * 1000.0; +} + +void QDeclarativeParticles::setVelocityDeviation(qreal velocity) +{ + Q_D(QDeclarativeParticles); + qreal realDev = velocity / 1000.0; + if(realDev == d->velocityDev) + return; + d->velocityDev = realDev; + emit velocityDeviationChanged(); +} + +/*! + \qmlproperty ParticleMotion Particles::motion + This property sets the type of motion to apply to the particles. + + When a particle is created it will have an initial direction and velocity. + The motion of the particle during its lifeSpan is then influenced by the + motion property. + + Default motion is ParticleMotionLinear. +*/ + +/*! + \property QDeclarativeParticles::motion + \brief sets the type of motion to apply to the particles. + + When a particle is created it will have an initial direction and velocity. + The motion of the particle during its lifeSpan is then influenced by the + motion property. + + Default motion is QDeclarativeParticleMotionLinear. +*/ +QDeclarativeParticleMotion *QDeclarativeParticles::motion() const +{ + Q_D(const QDeclarativeParticles); + return d->motion; +} + +void QDeclarativeParticles::setMotion(QDeclarativeParticleMotion *motion) +{ + Q_D(QDeclarativeParticles); + if (motion == d->motion) + return; + d->motion = motion; + emit motionChanged(); +} + +/*! + \qmlmethod Particles::burst(int count, int emissionRate) + + Initiates a burst of particles. + + This method takes two arguments. The first argument is the number + of particles to emit and the second argument is the emissionRate for the + burst. If the second argument is omitted, it is treated as -1. The burst + of particles has a separate emissionRate and count to the normal emission of + particles. The burst uses the same values as normal emission for all other + properties, including emissionVariance. + + The normal emission of particles will continue during the burst, however + the particles created by the burst count towards the maximum number used by + normal emission. To avoid this behavior, use two Particles elements. + +*/ +void QDeclarativeParticles::burst(int count, int emissionRate) +{ + Q_D(QDeclarativeParticles); + d->bursts << qMakePair(count, emissionRate); + if (d->clock.state() != QAbstractAnimation::Running) + d->clock.start(); +} + +void QDeclarativeParticlesPainter::updateSize() +{ + if (!d->componentComplete) + return; + + const int parentX = parentItem()->x(); + const int parentY = parentItem()->y(); + for (int i = 0; i < d->particles.count(); ++i) { + const QDeclarativeParticle &particle = d->particles.at(i); + if(particle.x > maxX) + maxX = particle.x; + if(particle.x < minX) + minX = particle.x; + if(particle.y > maxY) + maxY = particle.y; + if(particle.y < minY) + minY = particle.y; + } + + int myWidth = (int)(maxX-minX+0.5)+d->image.width(); + int myX = (int)(minX - parentX); + int myHeight = (int)(maxY-minY+0.5)+d->image.height(); + int myY = (int)(minY - parentY); + setWidth(myWidth); + setHeight(myHeight); + setX(myX); + setY(myY); +} + +void QDeclarativeParticles::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_UNUSED(p); + //painting is done by the ParticlesPainter, so it can have the right size +} + +void QDeclarativeParticlesPainter::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + if (d->image.isNull() || d->particles.isEmpty()) + return; + + const int myX = x() + parentItem()->x(); + const int myY = y() + parentItem()->y(); + + QVarLengthArray<QPainter::PixmapFragment, 256> pixmapData; + pixmapData.resize(d->particles.count()); + + const QRectF sourceRect = d->image.rect(); + qreal halfPWidth = sourceRect.width()/2.; + qreal halfPHeight = sourceRect.height()/2.; + for (int i = 0; i < d->particles.count(); ++i) { + const QDeclarativeParticle &particle = d->particles.at(i); + pixmapData[i].x = particle.x - myX + halfPWidth; + pixmapData[i].y = particle.y - myY + halfPHeight; + pixmapData[i].opacity = particle.opacity; + + //these never change + pixmapData[i].rotation = 0; + pixmapData[i].scaleX = 1; + pixmapData[i].scaleY = 1; + pixmapData[i].sourceLeft = sourceRect.left(); + pixmapData[i].sourceTop = sourceRect.top(); + pixmapData[i].width = sourceRect.width(); + pixmapData[i].height = sourceRect.height(); + } + p->drawPixmapFragments(pixmapData.data(), d->particles.count(), d->image); +} + +void QDeclarativeParticles::componentComplete() +{ + Q_D(QDeclarativeParticles); + QDeclarativeItem::componentComplete(); + if (d->count && d->emissionRate) { + d->paintItem->updateSize(); + d->clock.start(); + } + if (d->lifeSpanDev > d->lifeSpan) + d->lifeSpanDev = d->lifeSpan; +} + +QT_END_NAMESPACE diff --git a/src/imports/particles/qdeclarativeparticles_p.h b/src/imports/particles/qdeclarativeparticles_p.h new file mode 100644 index 00000000..0a2b6d8d --- /dev/null +++ b/src/imports/particles/qdeclarativeparticles_p.h @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPARTICLES_H +#define QDECLARATIVEPARTICLES_H + +#include <QtDeclarative/qdeclarativeitem.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeParticle; +class QDeclarativeParticles; +class QDeclarativeParticleMotion : public QObject +{ + Q_OBJECT +public: + QDeclarativeParticleMotion(QObject *parent=0); + + virtual void advance(QDeclarativeParticle &, int interval); + virtual void created(QDeclarativeParticle &); + virtual void destroy(QDeclarativeParticle &); +}; + +class QDeclarativeParticleMotionLinear : public QDeclarativeParticleMotion +{ + Q_OBJECT +public: + QDeclarativeParticleMotionLinear(QObject *parent=0) + : QDeclarativeParticleMotion(parent) {} + + virtual void advance(QDeclarativeParticle &, int interval); +}; + +class QDeclarativeParticleMotionGravity : public QDeclarativeParticleMotion +{ + Q_OBJECT + + Q_PROPERTY(qreal xattractor READ xAttractor WRITE setXAttractor NOTIFY xattractorChanged) + Q_PROPERTY(qreal yattractor READ yAttractor WRITE setYAttractor NOTIFY yattractorChanged) + Q_PROPERTY(qreal acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) +public: + QDeclarativeParticleMotionGravity(QObject *parent=0) + : QDeclarativeParticleMotion(parent), _xAttr(0.0), _yAttr(0.0), _accel(0.00005) {} + + qreal xAttractor() const { return _xAttr; } + void setXAttractor(qreal x); + + qreal yAttractor() const { return _yAttr; } + void setYAttractor(qreal y); + + qreal acceleration() const { return _accel * 1000000; } + void setAcceleration(qreal accel); + + virtual void advance(QDeclarativeParticle &, int interval); + +Q_SIGNALS: + void xattractorChanged(); + void yattractorChanged(); + void accelerationChanged(); + +private: + qreal _xAttr; + qreal _yAttr; + qreal _accel; +}; + +class QDeclarativeParticleMotionWander : public QDeclarativeParticleMotion +{ + Q_OBJECT +public: + QDeclarativeParticleMotionWander() + : QDeclarativeParticleMotion(), particles(0), _xvariance(0), _yvariance(0), _pace(100) {} + + virtual void advance(QDeclarativeParticle &, int interval); + virtual void created(QDeclarativeParticle &); + virtual void destroy(QDeclarativeParticle &); + + struct Data { + qreal x_targetV; + qreal y_targetV; + qreal x_peak; + qreal y_peak; + qreal x_var; + qreal y_var; + }; + + Q_PROPERTY(qreal xvariance READ xVariance WRITE setXVariance NOTIFY xvarianceChanged) + qreal xVariance() const { return _xvariance * 1000.0; } + void setXVariance(qreal var); + + Q_PROPERTY(qreal yvariance READ yVariance WRITE setYVariance NOTIFY yvarianceChanged) + qreal yVariance() const { return _yvariance * 1000.0; } + void setYVariance(qreal var); + + Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged) + qreal pace() const { return _pace * 1000.0; } + void setPace(qreal pace); + +Q_SIGNALS: + void xvarianceChanged(); + void yvarianceChanged(); + void paceChanged(); + +private: + QDeclarativeParticles *particles; + qreal _xvariance; + qreal _yvariance; + qreal _pace; +}; + +class QDeclarativeParticlesPrivate; +class QDeclarativeParticles : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged) + Q_PROPERTY(int emissionRate READ emissionRate WRITE setEmissionRate NOTIFY emissionRateChanged) + Q_PROPERTY(qreal emissionVariance READ emissionVariance WRITE setEmissionVariance NOTIFY emissionVarianceChanged) + Q_PROPERTY(int lifeSpan READ lifeSpan WRITE setLifeSpan NOTIFY lifeSpanChanged) + Q_PROPERTY(int lifeSpanDeviation READ lifeSpanDeviation WRITE setLifeSpanDeviation NOTIFY lifeSpanDeviationChanged) + Q_PROPERTY(int fadeInDuration READ fadeInDuration WRITE setFadeInDuration NOTIFY fadeInDurationChanged) + Q_PROPERTY(int fadeOutDuration READ fadeOutDuration WRITE setFadeOutDuration NOTIFY fadeOutDurationChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_PROPERTY(qreal angleDeviation READ angleDeviation WRITE setAngleDeviation NOTIFY angleDeviationChanged) + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity NOTIFY velocityChanged) + Q_PROPERTY(qreal velocityDeviation READ velocityDeviation WRITE setVelocityDeviation NOTIFY velocityDeviationChanged) + Q_PROPERTY(QDeclarativeParticleMotion *motion READ motion WRITE setMotion NOTIFY motionChanged) + Q_CLASSINFO("DefaultProperty", "motion") + +public: + QDeclarativeParticles(QDeclarativeItem *parent=0); + ~QDeclarativeParticles(); + + QUrl source() const; + void setSource(const QUrl &); + + int count() const; + void setCount(int cnt); + + int emissionRate() const; + void setEmissionRate(int); + + qreal emissionVariance() const; + void setEmissionVariance(qreal); + + int lifeSpan() const; + void setLifeSpan(int); + + int lifeSpanDeviation() const; + void setLifeSpanDeviation(int); + + int fadeInDuration() const; + void setFadeInDuration(int); + + int fadeOutDuration() const; + void setFadeOutDuration(int); + + qreal angle() const; + void setAngle(qreal); + + qreal angleDeviation() const; + void setAngleDeviation(qreal); + + qreal velocity() const; + void setVelocity(qreal); + + qreal velocityDeviation() const; + void setVelocityDeviation(qreal); + + QDeclarativeParticleMotion *motion() const; + void setMotion(QDeclarativeParticleMotion *); + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + +public Q_SLOTS: + void burst(int count, int emissionRate=-1); + +protected: + virtual void componentComplete(); + +Q_SIGNALS: + void sourceChanged(); + void countChanged(); + void emissionRateChanged(); + void emissionVarianceChanged(); + void lifeSpanChanged(); + void lifeSpanDeviationChanged(); + void fadeInDurationChanged(); + void fadeOutDurationChanged(); + void angleChanged(); + void angleDeviationChanged(); + void velocityChanged(); + void velocityDeviationChanged(); + void emittingChanged(); + void motionChanged(); + +private Q_SLOTS: + void imageLoaded(); + +private: + Q_DISABLE_COPY(QDeclarativeParticles) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeParticles) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeParticleMotion) +QML_DECLARE_TYPE(QDeclarativeParticleMotionLinear) +QML_DECLARE_TYPE(QDeclarativeParticleMotionGravity) +QML_DECLARE_TYPE(QDeclarativeParticleMotionWander) +QML_DECLARE_TYPE(QDeclarativeParticles) + +QT_END_HEADER + +#endif diff --git a/src/imports/particles/qmldir b/src/imports/particles/qmldir new file mode 100644 index 00000000..aeebd2c9 --- /dev/null +++ b/src/imports/particles/qmldir @@ -0,0 +1 @@ +plugin qmlparticlesplugin diff --git a/src/imports/qimportbase.pri b/src/imports/qimportbase.pri new file mode 100644 index 00000000..0f70030d --- /dev/null +++ b/src/imports/qimportbase.pri @@ -0,0 +1,36 @@ +symbian:include(../plugins/qpluginbase.pri) +TEMPLATE = lib +CONFIG += qt plugin + +win32|mac:!wince*:!win32-msvc:!macx-xcode:CONFIG += debug_and_release + +isEmpty(TARGETPATH) { + error("qimportbase.pri: You must provide a TARGETPATH!") +} +isEmpty(TARGET) { + error("qimportbase.pri: You must provide a TARGET!") +} + +QMLDIRFILE = $${_PRO_FILE_PWD_}/qmldir +copy2build.input = QMLDIRFILE +copy2build.output = $$QT_BUILD_TREE/imports/$$TARGETPATH/qmldir +!contains(TEMPLATE_PREFIX, vc):copy2build.variable_out = PRE_TARGETDEPS +copy2build.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} +copy2build.name = COPY ${QMAKE_FILE_IN} +copy2build.CONFIG += no_link +# `clean' should leave the build in a runnable state, which means it shouldn't delete qmldir +copy2build.CONFIG += no_clean +QMAKE_EXTRA_COMPILERS += copy2build + +TARGET = $$qtLibraryTarget($$TARGET) +contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols + +include(../qt_targets.pri) + +wince*:LIBS += $$QMAKE_LIBS_GUI + +symbian: { + TARGET.EPOCALLOWDLLDATA=1 + TARGET.CAPABILITY = All -Tcb + load(armcc_warnings) +} diff --git a/src/imports/shaders/glfunctions.h b/src/imports/shaders/glfunctions.h new file mode 100644 index 00000000..214143a5 --- /dev/null +++ b/src/imports/shaders/glfunctions.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GLFUNCTIONS_H +#define GLFUNCTIONS_H + +#ifndef QT_OPENGL_ES + +#ifndef Q_WS_MAC +# ifndef APIENTRYP +# ifdef APIENTRY +# define APIENTRYP APIENTRY * +# else +# define APIENTRY +# define APIENTRYP * +# endif +# endif +#else +# define APIENTRY +# define APIENTRYP * +#endif + +#define GL_TEXTURE0 0x84C0 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_BGRA 0x80E1 + +typedef void (APIENTRYP type_glActiveTexture)(GLenum texture); +typedef void (APIENTRYP type_glGenerateMipmap)(GLenum target); +typedef void (APIENTRYP type_glVertexAttribPointer)(GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid *); + +#define glActiveTexture ((type_glActiveTexture)QGLContext::currentContext()->getProcAddress(QLatin1String("glActiveTexture"))) +#define glGenerateMipmap ((type_glGenerateMipmap)QGLContext::currentContext()->getProcAddress(QLatin1String("glGenerateMipmap"))) +#define glVertexAttribPointer ((type_glVertexAttribPointer)QGLContext::currentContext()->getProcAddress(QLatin1String("glVertexAttribPointer"))) + +#endif + +#endif // GLFUNCTIONS_H diff --git a/src/imports/shaders/qmldir b/src/imports/shaders/qmldir new file mode 100644 index 00000000..b2a9de21 --- /dev/null +++ b/src/imports/shaders/qmldir @@ -0,0 +1,2 @@ +plugin qmlshadersplugin + diff --git a/src/imports/shaders/qmlshadersplugin_plugin.cpp b/src/imports/shaders/qmlshadersplugin_plugin.cpp new file mode 100644 index 00000000..613e1834 --- /dev/null +++ b/src/imports/shaders/qmlshadersplugin_plugin.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlshadersplugin_plugin.h" +#include "shadereffectitem.h" +#include "shadereffectsource.h" + +#include <QtDeclarative/qdeclarative.h> + +void qmlshaderspluginPlugin::registerTypes(const char *uri) +{ + qmlRegisterType<ShaderEffectItem>(uri, 1, 0, "ShaderEffectItem"); + qmlRegisterType<ShaderEffectSource>(uri, 1, 0, "ShaderEffectSource"); +} + +Q_EXPORT_PLUGIN2(qmlshadersplugin, qmlshaderspluginPlugin) + diff --git a/src/imports/shaders/qmlshadersplugin_plugin.h b/src/imports/shaders/qmlshadersplugin_plugin.h new file mode 100644 index 00000000..d1f7746c --- /dev/null +++ b/src/imports/shaders/qmlshadersplugin_plugin.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLSHADERSPLUGIN_PLUGIN_H +#define QMLSHADERSPLUGIN_PLUGIN_H + +#include <QtDeclarative/QDeclarativeExtensionPlugin> + +class qmlshaderspluginPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT + +public: + void registerTypes(const char *uri); +}; + +#endif // QMLSHADERSPLUGIN_PLUGIN_H + diff --git a/src/imports/shaders/scenegraph/qsggeometry.cpp b/src/imports/shaders/scenegraph/qsggeometry.cpp new file mode 100644 index 00000000..371b6e88 --- /dev/null +++ b/src/imports/shaders/scenegraph/qsggeometry.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsggeometry.h" + +QT_BEGIN_NAMESPACE + + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D() +{ + static Attribute data[] = { + { 0, 2, GL_FLOAT } + }; + static AttributeSet attrs = { 1, sizeof(float) * 2, data }; + return attrs; +} + + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D() +{ + static Attribute data[] = { + { 0, 2, GL_FLOAT }, + { 1, 2, GL_FLOAT } + }; + static AttributeSet attrs = { 2, sizeof(float) * 4, data }; + return attrs; +} + + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D() +{ + static Attribute data[] = { + { 0, 2, GL_FLOAT }, + { 1, 4, GL_UNSIGNED_BYTE } + }; + static AttributeSet attrs = { 2, 2 * sizeof(float) + 4 * sizeof(char), data }; + return attrs; +} + + + +QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes, + int vertexCount, + int indexCount, + int indexType) + : m_drawing_mode(GL_TRIANGLE_STRIP) + , m_vertex_count(0) + , m_index_count(0) + , m_index_type(indexType) + , m_attributes(attributes) + , m_data(0) + , m_index_data_offset(-1) + , m_owns_data(false) +{ + Q_ASSERT(m_attributes.count > 0); + Q_ASSERT(m_attributes.stride > 0); + + // Because allocate reads m_vertex_count, m_index_count and m_owns_data, these + // need to be set before calling allocate... + allocate(vertexCount, indexCount); +} + +QSGGeometry::~QSGGeometry() +{ + if (m_owns_data) + qFree(m_data); +} + +void *QSGGeometry::indexData() +{ + return m_index_data_offset < 0 + ? 0 + : ((char *) m_data + m_index_data_offset); +} + +const void *QSGGeometry::indexData() const +{ + return m_index_data_offset < 0 + ? 0 + : ((char *) m_data + m_index_data_offset); +} + +void QSGGeometry::setDrawingMode(GLenum mode) +{ + m_drawing_mode = mode; +} + +void QSGGeometry::allocate(int vertexCount, int indexCount) +{ + if (vertexCount == m_vertex_count && indexCount == m_index_count) + return; + + m_vertex_count = vertexCount; + m_index_count = indexCount; + + bool canUsePrealloc = m_index_count <= 0; + int vertexByteSize = m_attributes.stride * m_vertex_count; + + if (m_owns_data) + qFree(m_data); + + if (canUsePrealloc && vertexByteSize <= (int) sizeof(m_prealloc)) { + m_data = (void *) &m_prealloc[0]; + m_index_data_offset = -1; + m_owns_data = false; + } else { + Q_ASSERT(m_index_type == GL_UNSIGNED_INT || m_index_type == GL_UNSIGNED_SHORT); + int indexByteSize = indexCount * (m_index_type == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32)); + m_data = (void *) qMalloc(vertexByteSize + indexByteSize); + m_index_data_offset = vertexByteSize; + m_owns_data = true; + } + +} + +void QSGGeometry::updateRectGeometry(QSGGeometry *g, const QRectF &rect) +{ + Point2D *v = g->vertexDataAsPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + + v[1].x = rect.right(); + v[1].y = rect.top(); + + v[2].x = rect.left(); + v[2].y = rect.bottom(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); +} + +void QSGGeometry::updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &textureRect) +{ + TexturedPoint2D *v = g->vertexDataAsTexturedPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + v[0].tx = textureRect.left(); + v[0].ty = textureRect.top(); + + v[1].x = rect.right(); + v[1].y = rect.top(); + v[1].tx = textureRect.right(); + v[1].ty = textureRect.top(); + + v[2].x = rect.left(); + v[2].y = rect.bottom(); + v[2].tx = textureRect.left(); + v[2].ty = textureRect.bottom(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); + v[3].tx = textureRect.right(); + v[3].ty = textureRect.bottom(); +} + +QT_END_NAMESPACE diff --git a/src/imports/shaders/scenegraph/qsggeometry.h b/src/imports/shaders/scenegraph/qsggeometry.h new file mode 100644 index 00000000..9596898b --- /dev/null +++ b/src/imports/shaders/scenegraph/qsggeometry.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGGEOMETRY_H +#define QSGGEOMETRY_H + +#include <QtOpenGL/qgl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGGeometry +{ +public: + struct Attribute + { + int position; + int tupleSize; + int type; + }; + + struct AttributeSet { + int count; + int stride; + const Attribute *attributes; + }; + + struct Point2D { float x, y; }; + struct TexturedPoint2D { float x, y; float tx, ty; }; + struct ColoredPoint2D { float x, y; unsigned char r, g, b, a; }; + + static const AttributeSet &defaultAttributes_Point2D(); + static const AttributeSet &defaultAttributes_TexturedPoint2D(); + static const AttributeSet &defaultAttributes_ColoredPoint2D(); + + QSGGeometry(const QSGGeometry::AttributeSet &attribs, + int vertexCount, + int indexCount = 0, + int indexType = GL_UNSIGNED_SHORT); + ~QSGGeometry(); + + void setDrawingMode(GLenum mode); + inline GLenum drawingMode() const { return m_drawing_mode; } + + void allocate(int vertexCount, int indexCount = 0); + + int vertexCount() const { return m_vertex_count; } + + void *vertexData() { return m_data; } + inline Point2D *vertexDataAsPoint2D(); + inline TexturedPoint2D *vertexDataAsTexturedPoint2D(); + inline ColoredPoint2D *vertexDataAsColoredPoint2D(); + + inline const void *vertexData() const { return m_data; } + inline const Point2D *vertexDataAsPoint2D() const; + inline const TexturedPoint2D *vertexDataAsTexturedPoint2D() const; + inline const ColoredPoint2D *vertexDataAsColoredPoint2D() const; + + inline int indexType() const { return m_index_type; } + + int indexCount() const { return m_index_count; } + + void *indexData(); + inline uint *indexDataAsUInt(); + inline quint16 *indexDataAsUShort(); + + const void *indexData() const; + inline const uint *indexDataAsUInt() const; + inline const quint16 *indexDataAsUShort() const; + + inline int attributeCount() const { return m_attributes.count; } + inline const Attribute *attributes() const { return m_attributes.attributes; } + inline int stride() const { return m_attributes.stride; } + + static void updateRectGeometry(QSGGeometry *g, const QRectF &rect); + static void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &sourceRect); + +private: + int m_drawing_mode; + int m_vertex_count; + int m_index_count; + int m_index_type; + const AttributeSet &m_attributes; + void *m_data; + int m_index_data_offset; + + void *m_reserved_pointer; + + uint m_owns_data : 1; + uint m_reserved_bits : 31; + + float m_prealloc[16]; +}; + +inline uint *QSGGeometry::indexDataAsUInt() +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + return (uint *) indexData(); +} + +inline quint16 *QSGGeometry::indexDataAsUShort() +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + return (quint16 *) indexData(); +} + +inline const uint *QSGGeometry::indexDataAsUInt() const +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + return (uint *) indexData(); +} + +inline const quint16 *QSGGeometry::indexDataAsUShort() const +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + return (quint16 *) indexData(); +} + +inline QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() +{ + Q_ASSERT(m_attributes.count == 1); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].position == 0); + return (Point2D *) m_data; +} + +inline QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D() +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + return (TexturedPoint2D *) m_data; +} + +inline QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D() +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); + Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + return (ColoredPoint2D *) m_data; +} + +inline const QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() const +{ + Q_ASSERT(m_attributes.count == 1); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].position == 0); + return (const Point2D *) m_data; +} + +inline const QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D() const +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + return (const TexturedPoint2D *) m_data; +} + +inline const QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D() const +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); + Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + return (const ColoredPoint2D *) m_data; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGGEOMETRY_H diff --git a/src/imports/shaders/shadereffect.cpp b/src/imports/shaders/shadereffect.cpp new file mode 100644 index 00000000..97649eb6 --- /dev/null +++ b/src/imports/shaders/shadereffect.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shadereffect.h" +#include "shadereffectbuffer.h" +#include "shadereffectsource.h" + +#include <QDeclarativeItem> +#include <QPainter> +#include <QtOpenGL> + +static QTransform savedWorldTransform; + +ShaderEffect::ShaderEffect(QObject *parent) + : QGraphicsEffect(parent) + , m_changed(true) +{ +} + +ShaderEffect::~ShaderEffect() +{ +} + +void ShaderEffect::prepareBufferedDraw(QPainter *painter) +{ +#ifndef QT_NO_DYNAMIC_CAST + // This workaround needed because QGraphicsEffect seems to always utilize default painters worldtransform + // instead of the active painters worldtransform. + const ShaderEffectBuffer *effectBuffer = dynamic_cast<ShaderEffectBuffer*> (painter->device()); + if (effectBuffer) { + savedWorldTransform = painter->worldTransform() * savedWorldTransform; + painter->setWorldTransform(savedWorldTransform); + } else { + savedWorldTransform = painter->worldTransform(); + } +#else + Q_UNUSED(painter); +#endif +} + +void ShaderEffect::draw (QPainter *painter) +{ + const QGLContext *context = QGLContext::currentContext(); + + prepareBufferedDraw(painter); + + if (context) { + updateRenderTargets(); + } + + if (!context || m_renderTargets.count() == 0 || !hideOriginal()) + drawSource(painter); +} + +void ShaderEffect::updateRenderTargets() +{ + if (!m_changed) + return; + + m_changed = false; + + int count = m_renderTargets.count(); + for (int i = 0; i < count; i++) { + if (m_renderTargets[i]->isLive() || m_renderTargets[i]->isDirtyTexture()) { + m_renderTargets[i]->updateBackbuffer(); + ShaderEffectBuffer* target = m_renderTargets[i]->fbo(); + if (target && target->isValid() && target->width() > 0 && target->height() > 0) { + QPainter p(target); + p.setCompositionMode(QPainter::CompositionMode_Clear); + p.fillRect(QRect(QPoint(0, 0), target->size()), Qt::transparent); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + + QRectF sourceRect = m_renderTargets[i]->sourceRect(); + QSize textureSize = m_renderTargets[i]->textureSize(); + + qreal yflip = m_renderTargets[i]->isMirrored() ? -1.0 : 1.0; // flip y to match scenegraph, it also flips texturecoordinates + qreal xscale = 1.0; + qreal yscale = 1.0 * yflip; + + qreal leftMargin = 0.0; + qreal rightMargin = 0.0; + qreal topMargin = 0.0; + qreal bottomMargin = 0.0; + + qreal width = m_renderTargets[i]->sourceItem()->width(); + qreal height = m_renderTargets[i]->sourceItem()->height(); + + if (!sourceRect.isEmpty()) { + leftMargin = -sourceRect.left(); + rightMargin = sourceRect.right() - width; + topMargin = -sourceRect.top(); + bottomMargin = sourceRect.bottom() - height; + } + + if ((width + leftMargin + rightMargin) > 0 && (height + topMargin + bottomMargin) > 0) { + if (!textureSize.isEmpty()) { + qreal textureWidth = textureSize.width(); + qreal textureHeight = textureSize.height(); + + xscale = width / (width + leftMargin + rightMargin); + yscale = height / (height + topMargin + bottomMargin); + + p.translate(textureWidth / 2, textureHeight / 2); + p.scale(xscale, yscale * yflip); + p.translate(-textureWidth / 2, -textureHeight / 2); + p.scale(textureWidth / width, textureHeight / height); + } else { + xscale = width / (width + leftMargin + rightMargin); + yscale = height / (height + topMargin + bottomMargin); + + p.translate(width / 2, height / 2); + p.scale(xscale, yscale * yflip); + p.translate(-width / 2, -height / 2); + } + } + + drawSource(&p); + p.end(); + m_renderTargets[i]->markSceneGraphDirty(); + } + } + } +} + +void ShaderEffect::sourceChanged (ChangeFlags flags) +{ + Q_UNUSED(flags); + m_changed = true; +} + +void ShaderEffect::addRenderTarget(ShaderEffectSource *target) +{ + if (!m_renderTargets.contains(target)) + m_renderTargets.append(target); +} + +void ShaderEffect::removeRenderTarget(ShaderEffectSource *target) +{ + int index = m_renderTargets.indexOf(target); + if (index >= 0) + m_renderTargets.remove(index); + else + qWarning() << "ShaderEffect::removeRenderTarget - did not find target."; +} + +bool ShaderEffect::hideOriginal() const +{ + if (m_renderTargets.count() == 0) + return false; + + // Just like scenegraph version, if there is even one source that says "hide original" we hide it. + int count = m_renderTargets.count(); + for (int i = 0; i < count; i++) { + if (m_renderTargets[i]->hideSource()) + return true; + } + return false; +} diff --git a/src/imports/shaders/shadereffect.h b/src/imports/shaders/shadereffect.h new file mode 100644 index 00000000..ac61d2ea --- /dev/null +++ b/src/imports/shaders/shadereffect.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHADEREFFECT_H +#define SHADEREFFECT_H + +#include <QGraphicsEffect> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ShaderEffectSource; + +class ShaderEffect : public QGraphicsEffect +{ + Q_OBJECT + +public: + ShaderEffect(QObject *parent = 0); + ~ShaderEffect(); + void addRenderTarget(ShaderEffectSource *target); + void removeRenderTarget(ShaderEffectSource *target); + +protected: + virtual void draw (QPainter *painter); + virtual void sourceChanged (ChangeFlags flags); + +private: + void prepareBufferedDraw(QPainter *painter); + void updateRenderTargets(); + bool hideOriginal() const; + +public: + QVector<ShaderEffectSource*> m_renderTargets; + bool m_changed : 1; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // SHADEREFFECT_H diff --git a/src/imports/shaders/shadereffectbuffer.cpp b/src/imports/shaders/shadereffectbuffer.cpp new file mode 100644 index 00000000..415f9272 --- /dev/null +++ b/src/imports/shaders/shadereffectbuffer.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shadereffectbuffer.h" + +ShaderEffectBuffer::ShaderEffectBuffer(const QSize & size, const QGLFramebufferObjectFormat & format) + : QGLFramebufferObject(size, format) +{ +} + +ShaderEffectBuffer::~ShaderEffectBuffer() +{ +} + diff --git a/src/imports/shaders/shadereffectbuffer.h b/src/imports/shaders/shadereffectbuffer.h new file mode 100644 index 00000000..274a81ac --- /dev/null +++ b/src/imports/shaders/shadereffectbuffer.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHADEREFFECTBUFFER_H +#define SHADEREFFECTBUFFER_H + +#include <QtOpenGL> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ShaderEffectBuffer : public QGLFramebufferObject +{ +public: + ShaderEffectBuffer(const QSize &size, const QGLFramebufferObjectFormat &format); + ~ShaderEffectBuffer(); +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // SHADEREFFECTBUFFER_H diff --git a/src/imports/shaders/shadereffectitem.cpp b/src/imports/shaders/shadereffectitem.cpp new file mode 100644 index 00000000..3259c850 --- /dev/null +++ b/src/imports/shaders/shadereffectitem.cpp @@ -0,0 +1,955 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shadereffectitem.h" +#include "shadereffect.h" +#include "glfunctions.h" + +#include <QPainter> +#include <QtOpenGL> + +static const char qt_default_vertex_code[] = + "uniform highp mat4 qt_ModelViewProjectionMatrix;\n" + "attribute highp vec4 qt_Vertex;\n" + "attribute highp vec2 qt_MultiTexCoord0;\n" + "varying highp vec2 qt_TexCoord0;\n" + "void main(void)\n" + "{\n" + "qt_TexCoord0 = qt_MultiTexCoord0;\n" + "gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex;\n" + "}\n"; + +static const char qt_default_fragment_code[] = + "varying highp vec2 qt_TexCoord0;\n" + "uniform lowp sampler2D source;\n" + "void main(void)\n" + "{\n" + "gl_FragColor = texture2D(source, qt_TexCoord0.st);\n" + "}\n"; + +static const char qt_postion_attribute_name[] = "qt_Vertex"; +static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0"; +static const char qt_emptyAttributeName[] = ""; + + +/*! + \qmlclass ShaderEffectItem ShaderEffectItem + \ingroup qml-shader-elements + \brief The ShaderEffectItem object alters the output of given item with OpenGL shaders. + \inherits Item + + ShaderEffectItem is available in the \bold{Qt.labs.shaders 1.0} module. + \e {Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions.} + + This element provides preliminary support for embedding OpenGL shader code into QML, + and may be heavily changed or removed in later versions. + + Requirement for the use of shaders is that the application is either using + Qt OpenGL graphicssystem or is using OpenGL by setting QGLWidget as the viewport to QDeclarativeView (depending on which one is the recommened way in the targeted platform). + + ShaderEffectItem internal behaviour is such that during the paint event it first renders its + ShaderEffectSource items into a OpenGL framebuffer object which can be used as a texture. If the ShaderEffectSource is defined to be an image, + it is directly uploaded as a texture. The texture(s) containing the source pixelcontent are then bound to graphics + pipeline texture units. Finally a textured mesh is passed to the vertex- and fragmentshaders which + then produce the final output for the ShaderEffectItem. It is possible to alter the mesh structure by defining + the amount vertices it contains, but currently it is not possible to import complex 3D-models to be used as the mesh. + + It is possible to define one or more ShaderEffectItems to be a ShaderEffectSource for other ShaderEffectItems, but ShaderEffectItem + should never be declared as a child element of its source item(s) because it would cause circular loop in the painting. + + A standard set of vertex attributes are provided for the shaders: + + \list + \o qt_Vertex - The primary position of the vertex. + \o qt_MultiTexCoord0 - The texture co-ordinate at each vertex for texture unit 0. + \endlist + + Additionally following uniforms are available for shaders: + + \list + \o qt_Opacity - Effective opacity of the item. + \o qt_ModelViewProjectionMatrix - current 4x4 transformation matrix of the item. + \endlist + + Furthermore, it is possible to utilize automatic QML propertybinding into vertex- and fragment shader + uniforms. Conversions are done according to the table below: + + \table + \header + \o QML property + \o GLSL uniform + \row + \o property double foo: 1.0 + \o uniform highp float foo + \row + \o property real foo: 1.0 + \o uniform highp float foo + \row + \o property bool foo: true + \o uniform bool foo + \row + \o property int foo: 1 + \o uniform int foo + \row + \o property variant foo: Qt.point(1,1) + \o uniform highp vec2 foo + \row + \o property variant foo: Qt.size(1, 1) + \o uniform highp vec2 foo + \row + \o property variant foo: Qt.rect(1, 1, 2, 2) + \o uniform highp vec4 foo + \row + \o property color foo: "#00000000" + \o uniform lowp vec4 foo + \row + \o property variant foo: Qt.vector3d(1.0, 2.0, 0.0) + \o uniform highp vec3 foo + \row + \o property variant foo: ShaderEffectSource { SourceItem: bar } + \o uniform lowp sampler2D foo + \endtable + \note + The uniform precision definitions in the above table are not strict, it is possible to choose the uniform + precision based on what is the most suitable for the shader code for that particular uniform. + + + The below example uses fragment shader to create simple wiggly effect to a text label. + Automatic property binding takes care of binding the properties to the uniforms if their + names are identical. ShaderEffectSource referring to textLabel is bound to sampler2D uniform inside the fragment + shader code. + + \qml +import QtQuick 1.0 +import Qt.labs.shaders 1.0 + +Rectangle { + width: 300 + height: 300 + color: "black" + + Text { + id: textLabel + text: "Hello World" + anchors.centerIn: parent + font.pixelSize: 32 + color: "white" + + } + + ShaderEffectItem { + property variant source: ShaderEffectSource { sourceItem: textLabel; hideSource: true } + property real wiggleAmount: 0.005 + anchors.fill: textLabel + + fragmentShader: " + varying highp vec2 qt_TexCoord0; + uniform sampler2D source; + uniform highp float wiggleAmount; + void main(void) + { + highp vec2 wiggledTexCoord = qt_TexCoord0; + wiggledTexCoord.s += sin(4.0 * 3.141592653589 * wiggledTexCoord.t) * wiggleAmount; + gl_FragColor = texture2D(source, wiggledTexCoord.st); + } + " + } +} + \endqml + \image shaderexample.png + +*/ + +#ifdef Q_OS_SYMBIAN +#define OBSERVE_GL_CONTEXT_LOSS 1 +#endif + +ShaderEffectItem::ShaderEffectItem(QDeclarativeItem *parent) + : QDeclarativeItem(parent) + , m_program(0) + , m_meshResolution(1, 1) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) + , m_blending(true) + , m_program_dirty(true) + , m_active(true) + , m_respectsMatrix(false) + , m_respectsOpacity(false) + , m_checkedViewportUpdateMode(false) + , m_checkedOpenGL(false) + , m_checkedShaderPrograms(false) + , m_hasShaderPrograms(false) + , m_mirrored(false) + , m_defaultVertexShader(true) + , m_contextObserver(0) +{ + setFlag(QGraphicsItem::ItemHasNoContents, false); + connect(this, SIGNAL(visibleChanged()), this, SLOT(handleVisibilityChange())); + m_active = isVisible(); + +#ifndef OBSERVE_GL_CONTEXT_LOSS + m_program = new QGLShaderProgram(this); +#endif +} + +ShaderEffectItem::~ShaderEffectItem() +{ + reset(); + delete m_contextObserver; +} + + +/*! + \qmlproperty string ShaderEffectItem::fragmentShader + This property holds the OpenGL fragment shader code. + + The default fragment shader is following: + + \code + varying highp vec2 qt_TexCoord0; + uniform sampler2D source; + void main(void) + { + gl_FragColor = texture2D(source, qt_TexCoord0.st); + } + \endcode + +*/ + +void ShaderEffectItem::setFragmentShader(const QString &code) +{ + if (m_fragment_code.constData() == code.constData()) + return; + + m_fragment_code = code; + if (isComponentComplete()) { + reset(); + updateProperties(); + } + emit fragmentShaderChanged(); +} + +/*! + \qmlproperty string ShaderEffectItem::vertexShader + This property holds the OpenGL vertex shader code. + + The default vertex shader is following: + + \code + uniform highp mat4 qt_ModelViewProjectionMatrix; + attribute highp vec4 qt_Vertex; + attribute highp vec2 qt_MultiTexCoord0; + varying highp vec2 qt_TexCoord0; + void main(void) + { + qt_TexCoord0 = qt_MultiTexCoord0; + gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; + } + \endcode + +*/ + +void ShaderEffectItem::setVertexShader(const QString &code) +{ + if (m_vertex_code.constData() == code.constData()) + return; + + m_vertex_code = code; + m_defaultVertexShader = false; + if (isComponentComplete()) { + reset(); + updateProperties(); + } + emit vertexShaderChanged(); +} + +/*! + \qmlproperty bool ShaderEffectItem::blending + This property defines whether item is drawn using blending. + + If true, the RGBA pixel output from the fragment shader is blended with + the pixel RGBA-values already in the framebuffer. + + If false, fragment shader output is written to framebuffer as such. + + Usually drawing without blending is slightly faster, thus disabling blending + might be a good choice when item is used as a background element. + + \note + By default the pixel data in textures is stored in 32-bit premultiplied alpha format. + This should be taken into account when blending or reading the pixel values + in the fragment shader code. + + The default value is true. +*/ + +void ShaderEffectItem::setBlending(bool enable) +{ + if (m_blending == enable) + return; + + m_blending = enable; + m_changed = true; + emit blendingChanged(); +} + + +/*! + \qmlproperty QSize ShaderEffectItem::meshResolution + This property defines to how many triangles the item is divided into before its + vertices are passed to the vertex shader. + + Triangles are defined as triangle strips and the amount of triangles can be controlled + separately for x and y-axis. + + The default value is QSize(1,1). +*/ + +void ShaderEffectItem::setMeshResolution(const QSize &size) +{ + if (size == m_meshResolution) + return; + + m_meshResolution = size; + emit meshResolutionChanged(); + updateGeometry(); +} + +void ShaderEffectItem::componentComplete() +{ + updateProperties(); + QDeclarativeItem::componentComplete(); +} + +void ShaderEffectItem::checkViewportUpdateMode() +{ + if (!m_checkedViewportUpdateMode) { + QGraphicsScene *s = scene(); + if (s){ + QList<QGraphicsView*> views = s->views(); + for (int i = 0; i < views.count(); i++) { + if (views[i]->viewportUpdateMode() != QGraphicsView::FullViewportUpdate) { + qWarning() << "ShaderEffectItem::checkViewportUpdateMode - consider setting QGraphicsView::FullViewportUpdate mode with OpenGL!"; + } + } + } + m_checkedViewportUpdateMode = true; + } +} + +void ShaderEffectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + if (!m_active) return; + + const QGLContext *context = QGLContext::currentContext(); + + if (context) { + if (!m_checkedShaderPrograms) { + m_hasShaderPrograms = QGLShaderProgram::hasOpenGLShaderPrograms(context); + m_checkedShaderPrograms = true; + + if (!m_hasShaderPrograms) + qWarning() << "ShaderEffectItem::paint - Shader programs are not supported"; + } + + if ( !m_hasShaderPrograms ) + return; + + checkViewportUpdateMode(); + painter->save(); + painter->beginNativePainting(); + QMatrix4x4 combinedMatrix = QMatrix4x4(painter->transform()); + renderEffect(painter, combinedMatrix); + painter->endNativePainting(); + painter->restore(); + } else { + if (!m_checkedOpenGL) { + qWarning() << "ShaderEffectItem::paint - OpenGL not available"; + m_checkedOpenGL = true; + } + } +} + +void ShaderEffectItem::renderEffect(QPainter *painter, const QMatrix4x4 &matrix) +{ + if (!painter || !painter->device()) + return; + +#ifdef OBSERVE_GL_CONTEXT_LOSS + QGLContext *context = const_cast <QGLContext*> (QGLContext::currentContext()); + if (!m_program || !m_contextObserver || !m_contextObserver->isValid()) { + // Context has changed, re-create QGLShaderProgram + if (context) { + delete m_program; + m_program = 0; + + delete m_contextObserver; + m_contextObserver = 0; + + m_program = new QGLShaderProgram(this); + m_contextObserver = new QGLFramebufferObject(QSize(2,2)); + + if (!m_contextObserver || !m_program) { + delete m_program; + m_program = 0; + delete m_contextObserver; + m_contextObserver = 0; + qWarning() << "ShaderEffectItem::renderEffect - Creating QGLShaderProgram or QGLFrameBufferObject failed!"; + } + } + } +#endif + + if (!m_program) + return; + + if (!m_program->isLinked() || m_program_dirty) + updateShaderProgram(); + + m_program->bind(); + + QMatrix4x4 combinedMatrix; + combinedMatrix.scale(2.0 / painter->device()->width(), -2.0 / painter->device()->height(), 1.0); + combinedMatrix.translate(-painter->device()->width() / 2.0, -painter->device()->height() / 2.0 ); + combinedMatrix *= matrix; + updateEffectState(combinedMatrix); + + for (int i = 0; i < m_attributeNames.size(); ++i) { + m_program->enableAttributeArray(m_geometry.attributes()[i].position); + } + + bindGeometry(); + + // Optimization, disable depth test when we know we don't need it. + if (m_defaultVertexShader) { + glDepthMask(false); + glDisable(GL_DEPTH_TEST); + } else { + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_GREATER); + glDepthMask(true); +#if defined(QT_OPENGL_ES) + glClearDepthf(0); +#else + glClearDepth(0); +#endif + glClearColor(0, 0, 0, 0); + glClear(GL_DEPTH_BUFFER_BIT); + } + + if (m_blending){ + glEnable(GL_BLEND); + glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } + + if (m_geometry.indexCount()) + glDrawElements(m_geometry.drawingMode(), m_geometry.indexCount(), m_geometry.indexType(), m_geometry.indexData()); + else + glDrawArrays(m_geometry.drawingMode(), 0, m_geometry.vertexCount()); + + glDepthMask(false); + glDisable(GL_DEPTH_TEST); + + for (int i = 0; i < m_attributeNames.size(); ++i) + m_program->disableAttributeArray(m_geometry.attributes()[i].position); +} + +void ShaderEffectItem::updateEffectState(const QMatrix4x4 &matrix) +{ + if (!m_program) + return; + + for (int i = m_sources.size() - 1; i >= 0; --i) { + const ShaderEffectItem::SourceData &source = m_sources.at(i); + if (!source.source) + continue; + + glActiveTexture(GL_TEXTURE0 + i); + source.source->bind(); + } + + if (m_respectsOpacity) + m_program->setUniformValue("qt_Opacity", static_cast<float> (effectiveOpacity())); + + if (m_respectsMatrix){ + m_program->setUniformValue("qt_ModelViewProjectionMatrix", matrix); + } + + QSet<QByteArray>::const_iterator it; + for (it = m_uniformNames.begin(); it != m_uniformNames.end(); ++it) { + const QByteArray &name = *it; + QVariant v = property(name.constData()); + + switch (v.type()) { + case QVariant::Color: + m_program->setUniformValue(name.constData(), qvariant_cast<QColor>(v)); + break; + case QVariant::Double: + m_program->setUniformValue(name.constData(), (float) qvariant_cast<double>(v)); + break; + case QVariant::Transform: + m_program->setUniformValue(name.constData(), qvariant_cast<QTransform>(v)); + break; + case QVariant::Int: + m_program->setUniformValue(name.constData(), GLint(v.toInt())); + break; + case QVariant::Bool: + m_program->setUniformValue(name.constData(), GLint(v.toBool())); + break; + case QVariant::Size: + case QVariant::SizeF: + m_program->setUniformValue(name.constData(), v.toSizeF()); + break; + case QVariant::Point: + case QVariant::PointF: + m_program->setUniformValue(name.constData(), v.toPointF()); + break; + case QVariant::Rect: + case QVariant::RectF: + { + QRectF r = v.toRectF(); + m_program->setUniformValue(name.constData(), r.x(), r.y(), r.width(), r.height()); + } + break; + case QVariant::Vector3D: + m_program->setUniformValue(name.constData(), qvariant_cast<QVector3D>(v)); + break; + default: + break; + } + } +} + +static inline int size_of_type(GLenum type) +{ + static int sizes[] = { + sizeof(char), + sizeof(unsigned char), + sizeof(short), + sizeof(unsigned short), + sizeof(int), + sizeof(unsigned int), + sizeof(float), + 2, + 3, + 4, + sizeof(double) + }; + return sizes[type - GL_BYTE]; +} + +void ShaderEffectItem::bindGeometry() +{ + if (!m_program) + return; + + char const *const *attrNames = m_attributeNames.constData(); + int offset = 0; + for (int j = 0; j < m_attributeNames.size(); ++j) { + if (!*attrNames[j]) + continue; + Q_ASSERT_X(j < m_geometry.attributeCount(), "ShaderEffectItem::bindGeometry()", "Geometry lacks attribute required by material"); + const QSGGeometry::Attribute &a = m_geometry.attributes()[j]; + Q_ASSERT_X(j == a.position, "ShaderEffectItem::bindGeometry()", "Geometry does not have continuous attribute positions"); +#if defined(QT_OPENGL_ES_2) + GLboolean normalize = a.type != GL_FLOAT; +#else + GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE; +#endif + if (normalize) + qWarning() << "ShaderEffectItem::bindGeometry() - non supported attribute type!"; + + m_program->setAttributeArray(a.position, (GLfloat*) (((char*) m_geometry.vertexData()) + offset), a.tupleSize, m_geometry.stride()); + //glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, m_geometry.stride(), (char *) m_geometry.vertexData() + offset); + offset += a.tupleSize * size_of_type(a.type); + } +} + +void ShaderEffectItem::updateGeometry() +{ + QRectF srcRect(0, 1, 1, -1); + + if (m_mirrored) + srcRect = QRectF(0, 0, 1, 1); + + QRectF dstRect = QRectF(0,0, width(), height()); + + int vmesh = m_meshResolution.height(); + int hmesh = m_meshResolution.width(); + + QSGGeometry *g = &m_geometry; + if (vmesh == 1 && hmesh == 1) { + if (g->vertexCount() != 4) + g->allocate(4); + QSGGeometry::updateTexturedRectGeometry(g, dstRect, srcRect); + return; + } + + g->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2)); + + QSGGeometry::TexturedPoint2D *vdata = g->vertexDataAsTexturedPoint2D(); + + for (int iy = 0; iy <= vmesh; ++iy) { + float fy = iy / float(vmesh); + float y = float(dstRect.top()) + fy * float(dstRect.height()); + float ty = float(srcRect.top()) + fy * float(srcRect.height()); + for (int ix = 0; ix <= hmesh; ++ix) { + float fx = ix / float(hmesh); + vdata->x = float(dstRect.left()) + fx * float(dstRect.width()); + vdata->y = y; + vdata->tx = float(srcRect.left()) + fx * float(srcRect.width()); + vdata->ty = ty; + ++vdata; + } + } + + quint16 *indices = (quint16 *)g->indexDataAsUShort(); + int i = 0; + for (int iy = 0; iy < vmesh; ++iy) { + *(indices++) = i + hmesh + 1; + for (int ix = 0; ix <= hmesh; ++ix, ++i) { + *(indices++) = i + hmesh + 1; + *(indices++) = i; + } + *(indices++) = i - 1; + } +} + +void ShaderEffectItem::setActive(bool enable) +{ + if (m_active == enable) + return; + + if (m_active) { + for (int i = 0; i < m_sources.size(); ++i) { + ShaderEffectSource *source = m_sources.at(i).source; + if (!source) + continue; + disconnect(source, SIGNAL(repaintRequired()), this, SLOT(markDirty())); + source->derefFromEffectItem(); + } + } + + m_active = enable; + + if (m_active) { + for (int i = 0; i < m_sources.size(); ++i) { + ShaderEffectSource *source = m_sources.at(i).source; + if (!source) + continue; + source->refFromEffectItem(); + connect(source, SIGNAL(repaintRequired()), this, SLOT(markDirty())); + } + } + + // QGLShaderProgram is deleted when not active (to minimize GPU memory usage). +#ifdef OBSERVE_GL_CONTEXT_LOSS + if (!m_active && m_program) { + delete m_program; + m_program = 0; + delete m_contextObserver; + m_contextObserver = 0; + } +#endif + + emit activeChanged(); + markDirty(); +} + +void ShaderEffectItem::preprocess() +{ + for (int i = 0; i < m_sources.size(); ++i) { + ShaderEffectSource *source = m_sources.at(i).source; + if (source) + source->updateBackbuffer(); + } +} + +void ShaderEffectItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (newGeometry.size() != oldGeometry.size()) + updateGeometry(); + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +void ShaderEffectItem::changeSource(int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + QVariant v = property(m_sources.at(index).name.constData()); + setSource(v, index); +} + +void ShaderEffectItem::markDirty() { + update(); +} + +void ShaderEffectItem::setSource(const QVariant &var, int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + + SourceData &source = m_sources[index]; + + source.source = 0; + source.item = 0; + if (var.isNull()) { + return; + } else if (!qVariantCanConvert<QObject *>(var)) { + qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); + return; + } + + QObject *obj = qVariantValue<QObject *>(var); + + source.source = qobject_cast<ShaderEffectSource *>(obj); + source.item = qobject_cast<QDeclarativeItem *>(obj); + + if (!source.item) + qWarning("Could not assign property '%s', did not implement QDeclarativeItem.", source.name.constData()); + + if (!source.source) + qWarning("Could not assign property '%s', did not implement ShaderEffectSource.", source.name.constData()); + + // TODO: Find better solution. + // 'source.item' needs a canvas to get a scenegraph node. + // The easiest way to make sure it gets a canvas is to + // make it a part of the same item tree as 'this'. + if (source.item && source.item->parentItem() == 0) { + source.item->setParentItem(this); + // Unlike in scenegraph, we cannot set item invisible here because qgraphicsview would optimize it away. + } + + // Unlike in scenegraph, ref counting is used to optimize memory consumption. Sources themself may free fbos when not referenced. + if (m_active && source.source) { + source.source->refFromEffectItem(); + connect(source.source, SIGNAL(repaintRequired()), this, SLOT(markDirty())); + } +} + +void ShaderEffectItem::disconnectPropertySignals() +{ + disconnect(this, 0, this, SLOT(markDirty())); + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + disconnect(this, 0, source.mapper, 0); + disconnect(source.mapper, 0, this, 0); + } +} + +void ShaderEffectItem::connectPropertySignals() +{ + QSet<QByteArray>::const_iterator it; + for (it = m_uniformNames.begin(); it != m_uniformNames.end(); ++it) { + int pi = metaObject()->indexOfProperty(it->constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + if (!mp.hasNotifySignal()) + qWarning("ShaderEffectItem: property '%s' does not have notification method!", it->constData()); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, this, SLOT(markDirty())); + } else { + qWarning("ShaderEffectItem: '%s' does not have a matching property!", it->constData()); + } + } + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + int pi = metaObject()->indexOfProperty(source.name.constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, source.mapper, SLOT(map())); + source.mapper->setMapping(this, i); + connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); + } else { + qWarning("ShaderEffectItem: '%s' does not have a matching source!", source.name.constData()); + } + } +} + +void ShaderEffectItem::reset() +{ + disconnectPropertySignals(); + + if (m_program) + m_program->removeAllShaders(); + + m_attributeNames.clear(); + m_uniformNames.clear(); + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + if (m_active && source.source) + source.source->derefFromEffectItem(); + delete source.mapper; + } + + m_sources.clear(); + m_program_dirty = true; +} + +void ShaderEffectItem::updateProperties() +{ + QString vertexCode = m_vertex_code; + QString fragmentCode = m_fragment_code; + + if (vertexCode.isEmpty()) + vertexCode = qt_default_vertex_code; + + if (fragmentCode.isEmpty()) + fragmentCode = qt_default_fragment_code; + + lookThroughShaderCode(vertexCode); + lookThroughShaderCode(fragmentCode); + + if (!m_attributeNames.contains(qt_postion_attribute_name)) + qWarning("ShaderEffectItem: Missing reference to \'%s\'.", qt_postion_attribute_name); + if (!m_attributeNames.contains(qt_texcoord_attribute_name)) + qWarning("ShaderEffectItem: Missing reference to \'%s\'.", qt_texcoord_attribute_name); + if (!m_respectsMatrix) + qWarning("ShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'."); + + for (int i = 0; i < m_sources.size(); ++i) { + QVariant v = property(m_sources.at(i).name); + setSource(v, i); // Property exists. + } + + connectPropertySignals(); +} + +void ShaderEffectItem::updateShaderProgram() +{ + if (!m_program) + return; + + QString vertexCode = m_vertex_code; + QString fragmentCode = m_fragment_code; + + if (vertexCode.isEmpty()) + vertexCode = QString::fromLatin1(qt_default_vertex_code); + + if (fragmentCode.isEmpty()) + fragmentCode = QString::fromLatin1(qt_default_fragment_code); + + m_program->addShaderFromSourceCode(QGLShader::Vertex, vertexCode); + m_program->addShaderFromSourceCode(QGLShader::Fragment, fragmentCode); + + for (int i = 0; i < m_attributeNames.size(); ++i) { + m_program->bindAttributeLocation(m_attributeNames.at(i), m_geometry.attributes()[i].position); + } + + if (!m_program->link()) { + qWarning("ShaderEffectItem: Shader compilation failed:"); + qWarning() << m_program->log(); + } + + if (!m_attributeNames.contains(qt_postion_attribute_name)) + qWarning("ShaderEffectItem: Missing reference to \'qt_Vertex\'."); + if (!m_attributeNames.contains(qt_texcoord_attribute_name)) + qWarning("ShaderEffectItem: Missing reference to \'qt_MultiTexCoord0\'."); + if (!m_respectsMatrix) + qWarning("ShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'."); + + if (m_program->isLinked()) { + m_program->bind(); + for (int i = 0; i < m_sources.size(); ++i) + m_program->setUniformValue(m_sources.at(i).name.constData(), (GLint) i); + } + + m_program_dirty = false; +} + +void ShaderEffectItem::lookThroughShaderCode(const QString &code) +{ + // Regexp for matching attributes and uniforms. + // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name> + static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); + Q_ASSERT(re.isValid()); + + int pos = -1; + + //QString wideCode = QString::fromLatin1(code.constData(), code.size()); + QString wideCode = code; + + while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { + QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute + QByteArray type = re.cap(2).toLatin1(); // type + QByteArray name = re.cap(3).toLatin1(); // variable name + + if (decl == "attribute") { + if (name == qt_postion_attribute_name) { + m_attributeNames.insert(0, qt_postion_attribute_name); + } else if (name == "qt_MultiTexCoord0") { + if (m_attributeNames.at(0) == 0) { + m_attributeNames.insert(0, qt_emptyAttributeName); + } + m_attributeNames.insert(1, qt_texcoord_attribute_name); + } else { + // TODO: Support user defined attributes. + qWarning("ShaderEffectItem: Attribute \'%s\' not recognized.", name.constData()); + } + } else { + Q_ASSERT(decl == "uniform"); + + if (name == "qt_ModelViewProjectionMatrix") { + m_respectsMatrix = true; + } else if (name == "qt_Opacity") { + m_respectsOpacity = true; + } else { + m_uniformNames.insert(name); + if (type == "sampler2D") { + SourceData d; + d.mapper = new QSignalMapper; + d.source = 0; + d.name = name; + d.item = 0; + m_sources.append(d); + } + } + } + } +} + +void ShaderEffectItem::handleVisibilityChange() +{ + setActive(isVisible()); +} diff --git a/src/imports/shaders/shadereffectitem.h b/src/imports/shaders/shadereffectitem.h new file mode 100644 index 00000000..a5581b44 --- /dev/null +++ b/src/imports/shaders/shadereffectitem.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHADEREFFECTITEM_H +#define SHADEREFFECTITEM_H + +#include <QDeclarativeItem> +#include <QtOpenGL> +#include "shadereffectsource.h" +#include "scenegraph/qsggeometry.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ShaderEffectItem : public QDeclarativeItem +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QString fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged) + Q_PROPERTY(QString vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged) + Q_PROPERTY(bool blending READ blending WRITE setBlending NOTIFY blendingChanged) + Q_PROPERTY(QSize meshResolution READ meshResolution WRITE setMeshResolution NOTIFY meshResolutionChanged) + +public: + ShaderEffectItem(QDeclarativeItem* parent = 0); + ~ShaderEffectItem(); + + virtual void componentComplete(); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + QString fragmentShader() const { return m_fragment_code; } + void setFragmentShader(const QString &code); + + QString vertexShader() const { return m_vertex_code; } + void setVertexShader(const QString &code); + + bool blending() const { return m_blending; } + void setBlending(bool enable); + + QSize meshResolution() const { return m_meshResolution; } + void setMeshResolution(const QSize &size); + + void preprocess(); + +Q_SIGNALS: + void fragmentShaderChanged(); + void vertexShaderChanged(); + void blendingChanged(); + void activeChanged(); + void meshResolutionChanged(); + +protected: + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + +private Q_SLOTS: + void changeSource(int index); + void handleVisibilityChange(); + void markDirty(); + +private: + void checkViewportUpdateMode(); + void renderEffect(QPainter *painter, const QMatrix4x4 &matrix); + void updateEffectState(const QMatrix4x4 &matrix); + void updateGeometry(); + void bindGeometry(); + void setSource(const QVariant &var, int index); + void disconnectPropertySignals(); + void connectPropertySignals(); + void reset(); + void updateProperties(); + void updateShaderProgram(); + void lookThroughShaderCode(const QString &code); + bool active() const { return m_active; } + void setActive(bool enable); + +private: + QString m_fragment_code; + QString m_vertex_code; + QGLShaderProgram* m_program; + QVector<const char *> m_attributeNames; + QSet<QByteArray> m_uniformNames; + QSize m_meshResolution; + QSGGeometry m_geometry; + + struct SourceData + { + QSignalMapper *mapper; + QPointer<ShaderEffectSource> source; + QPointer<QDeclarativeItem> item; + QByteArray name; + }; + + QVector<SourceData> m_sources; + + bool m_changed : 1; + bool m_blending : 1; + bool m_program_dirty : 1; + bool m_active : 1; + bool m_respectsMatrix : 1; + bool m_respectsOpacity : 1; + bool m_checkedViewportUpdateMode : 1; + bool m_checkedOpenGL : 1; + bool m_checkedShaderPrograms : 1; + bool m_hasShaderPrograms : 1; + bool m_mirrored : 1; + bool m_defaultVertexShader : 1; + + QGLFramebufferObject* m_contextObserver; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // SHADEREFFECTITEM_H diff --git a/src/imports/shaders/shadereffectsource.cpp b/src/imports/shaders/shadereffectsource.cpp new file mode 100644 index 00000000..aa434e1c --- /dev/null +++ b/src/imports/shaders/shadereffectsource.cpp @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shadereffectsource.h" +#include "shadereffectbuffer.h" +#include "shadereffect.h" +#include "glfunctions.h" + +#include <QtOpenGL> + +/*! + \qmlclass ShaderEffectSource ShaderEffectSource + \ingroup qml-shader-elements + \brief The ShaderEffectSource object encapsulates the source content for the ShaderEffectItem. + + ShaderEffectSource is available in the \bold{Qt.labs.shaders 1.0} module. + \e {Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions.} + + This element provides preliminary support for OpenGL shaders in QML, + and may be heavily changed or removed in later versions. + + Requirement for the ability to use of shaders is that the application is either using + opengl graphicssystem or has set QGLWidget as the viewport to QDeclarativeView (recommended way). + + ShaderEffectSource object encapsulates the source content so that it can be utilized in ShaderEffectItem. + Source content can be a live QML object tree, or a snapshot of QML object tree. + +*/ + +ShaderEffectSource::ShaderEffectSource(QDeclarativeItem *parent) + : QDeclarativeItem(parent) + , m_sourceItem(0) + , m_wrapMode(ClampToEdge) + , m_sourceRect(0, 0, 0, 0) + , m_textureSize(0, 0) + , m_format(RGBA) + , m_size(0, 0) + , m_fbo(0) + , m_multisampledFbo(0) + , m_refs(0) + , m_dirtyTexture(true) + , m_dirtySceneGraph(true) + , m_multisamplingSupported(false) + , m_checkedForMultisamplingSupport(false) + , m_live(true) + , m_hideSource(false) + , m_mirrored(false) +{ +} + +ShaderEffectSource::~ShaderEffectSource() +{ + if (m_refs && m_sourceItem) + detachSourceItem(); + + delete m_fbo; + delete m_multisampledFbo; +} + +/*! + \qmlproperty Item ShaderEffectSource::sourceItem + This property holds the Item which is used as the source for the shader effect. + If the item has children, those are included as well. + + \note When source item content is passed to the ShaderEffectItem(s), it is always clipped to the boundingrect of the + sourceItem regardless of its clipping property. +*/ + +void ShaderEffectSource::setSourceItem(QDeclarativeItem *item) +{ + if (item == m_sourceItem) + return; + + if (m_sourceItem) { + disconnect(m_sourceItem, SIGNAL(widthChanged()), this, SLOT(markSourceSizeDirty())); + disconnect(m_sourceItem, SIGNAL(heightChanged()), this, SLOT(markSourceSizeDirty())); + + if (m_refs) + detachSourceItem(); + } + + m_sourceItem = item; + + if (m_sourceItem) { + + // Must have some item as parent + if (m_sourceItem->parentItem() == 0) + m_sourceItem->setParentItem(this); + + if (m_refs) + attachSourceItem(); + + connect(m_sourceItem, SIGNAL(widthChanged()), this, SLOT(markSourceSizeDirty())); + connect(m_sourceItem, SIGNAL(heightChanged()), this, SLOT(markSourceSizeDirty())); + } + + updateSizeAndTexture(); + emit sourceItemChanged(); + emit repaintRequired(); +} + +/*! + \qmlproperty QRectF ShaderEffectSource::sourceRect + This property can be used to specify margins for the source content. + + If other value than Qt.rect(0,0,0,0) is assigned to this property, it is interpreted as + specifying a relative source rectangle for the source content. + + For example, setting Qt.rect(-10.0, -10.0, 120.0, 120.0) for a source that has width and height + of 100 pixels would produce 10 pixels margins to each side of the source. + + Margins are useful when the original content is wanted to be spread outside the original source area, + like when creating a dropshadow with the shader or in other similar effects. + + The default value is Qt.rect(0,0,0,0). +*/ + +void ShaderEffectSource::setSourceRect(const QRectF &rect) +{ + if (rect == m_sourceRect) + return; + m_sourceRect = rect; + updateSizeAndTexture(); + emit sourceRectChanged(); + emit repaintRequired(); + + m_dirtyTexture = true; + markSourceItemDirty(); +} + +/*! + \qmlproperty QSize ShaderEffectSource::textureSize + This property holds the size for the texture containing the source content. + + If value QSize(0,0) is assigned to this property, texture is resized + according to the source size. Otherwise source content is scaled to + the given size. + + The default value is QSize(0,0). +*/ + +void ShaderEffectSource::setTextureSize(const QSize &size) +{ + if (size == m_textureSize) + return; + + m_textureSize = size; + updateSizeAndTexture(); + emit textureSizeChanged(); + emit repaintRequired(); + + m_dirtyTexture = true; + markSourceItemDirty(); +} + +/*! + \qmlproperty bool ShaderEffectSource::live + This property holds the optimization flag to define whether the source item content is changing or + static. + + If value true is assigned to this property, source item content is re-rendered into a + texture for every frame. Setting the value to false improves the performance as it skips + rendering the source item (and its chidleren) and instead immediately passes the previously + rendered and cached texture to the shaders. + + The default value is true. +*/ + +void ShaderEffectSource::setLive(bool s) +{ + if (s == m_live) + return; + + m_live = s; + + emit liveChanged(); + emit repaintRequired(); +} + +/*! + \qmlproperty bool ShaderEffectSource::hideSource + This property holds the flag to define whether the original source item is + hidden when the effect item is drawn. + + The default value is false. +*/ + +void ShaderEffectSource::setHideSource(bool hide) +{ + if (hide == m_hideSource) + return; + + m_hideSource = hide; + + emit hideSourceChanged(); + emit repaintRequired(); +} + +/*! + \qmlproperty enumeration ShaderEffectSource::wrapMode + + This property defines the wrap parameter for the source after it has been mapped as a texture. + + \list + \o ShaderEffectSource.ClampToEdge - Causes texturecoordinates to be clamped to the range [ 1/2*N , 1 - 1/2*N ], where N is the texture width. + \o ShaderEffectSource.RepeatHorizontally - Causes the integer part of the horizontal texturecoordinate to be ignored; the GL uses only the fractional part, thereby creating a horizontal repeating pattern. + \o ShaderEffectSource.RepeatVertically - Causes the integer part of the vertical texturecoordinate to be ignored; the GL uses only the fractional part, thereby creating a vertical repeating pattern. + \o ShaderEffectSource.Repeat - Causes the integer part of both the horizontal and vertical texturecoordinates to be ignored; the GL uses only the fractional part, thereby creating a repeating pattern. + \endlist + + The default value is ClampToEdge. + +*/ + +void ShaderEffectSource::setWrapMode(WrapMode mode) +{ + if (mode == m_wrapMode) + return; + + m_wrapMode = mode; + emit wrapModeChanged(); + + m_dirtyTexture = true; + markSourceItemDirty(); +} + +/*! + \qmlmethod ShaderEffectSource::grab() + + Repaints the source item content into the texture. + + This method is useful when ShaderEffectSource::live has been set to false and + the changes in the source item content is desired to be made visible for the shaders. + +*/ + +void ShaderEffectSource::grab() +{ + m_dirtyTexture = true; + emit repaintRequired(); +} + +void ShaderEffectSource::bind() +{ + GLint filtering = smooth() ? GL_LINEAR : GL_NEAREST; + GLuint hwrap = (m_wrapMode == Repeat || m_wrapMode == RepeatHorizontally) ? GL_REPEAT : GL_CLAMP_TO_EDGE; + GLuint vwrap = (m_wrapMode == Repeat || m_wrapMode == RepeatVertically) ? GL_REPEAT : GL_CLAMP_TO_EDGE; + +#if !defined(QT_OPENGL_ES_2) + glEnable(GL_TEXTURE_2D); +#endif + + if (m_fbo && m_fbo->isValid()) { + glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); + } else { + m_dirtyTexture = true; + emit repaintRequired(); + markSourceItemDirty(); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth() ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, hwrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vwrap); +} + +void ShaderEffectSource::refFromEffectItem() +{ + if (m_refs++ == 0) { + attachSourceItem(); + emit activeChanged(); + } +} + +void ShaderEffectSource::derefFromEffectItem() +{ + if (--m_refs == 0) { + detachSourceItem(); + emit activeChanged(); + } + Q_ASSERT(m_refs >= 0); +} + +void ShaderEffectSource::updateBackbuffer() +{ + if (!m_sourceItem || !QGLContext::currentContext()) + return; + + // Multisampling is not (for now) supported. + QSize size = QSize(m_sourceItem->width(), m_sourceItem->height()); + if (!m_textureSize.isEmpty()) + size = m_textureSize; + + if (size.height() > 0 && size.width() > 0) { + QGLFramebufferObjectFormat format; + format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + format.setInternalTextureFormat(m_format); + + if (!m_fbo) { + m_fbo = new ShaderEffectBuffer(size, format); + } else { + if (!m_fbo->isValid() || m_fbo->size() != size || m_fbo->format().internalTextureFormat() != GLenum(m_format)) { + delete m_fbo; + m_fbo = 0; + m_fbo = new ShaderEffectBuffer(size, format); + } + } + } + + // Note that real update for the source content happens in shadereffect.cpp + m_dirtyTexture = false; +} + +void ShaderEffectSource::markSceneGraphDirty() +{ + m_dirtySceneGraph = true; + emit repaintRequired(); +} + +void ShaderEffectSource::markSourceSizeDirty() +{ + Q_ASSERT(m_sourceItem); + if (m_textureSize.isEmpty()) + updateSizeAndTexture(); + if (m_refs) + emit repaintRequired(); +} + +void ShaderEffectSource::markSourceItemDirty() +{ + m_dirtyTexture = true; + if (m_sourceItem) { + ShaderEffect* effect = qobject_cast<ShaderEffect*> (m_sourceItem->graphicsEffect()); + if (effect) + effect->m_changed = true; + } +} + +void ShaderEffectSource::updateSizeAndTexture() +{ + if (m_sourceItem) { + QSize size = m_textureSize; + if (size.isEmpty()) + size = QSize(m_sourceItem->width(), m_sourceItem->height()); + if (size.width() < 1) + size.setWidth(1); + if (size.height() < 1) + size.setHeight(1); + if (m_fbo && (m_fbo->size() != size || !m_fbo->isValid())) { + delete m_fbo; + m_fbo = 0; + delete m_multisampledFbo; + m_fbo = m_multisampledFbo = 0; + } + if (m_size.width() != size.width()) { + m_size.setWidth(size.width()); + emit widthChanged(); + } + if (m_size.height() != size.height()) { + m_size.setHeight(size.height()); + emit heightChanged(); + } + m_dirtyTexture = true; + } else { + if (m_size.width() != 0) { + m_size.setWidth(0); + emit widthChanged(); + } + if (m_size.height() != 0) { + m_size.setHeight(0); + emit heightChanged(); + } + } +} + +void ShaderEffectSource::attachSourceItem() +{ + if (!m_sourceItem) + return; + + ShaderEffect *effect = qobject_cast<ShaderEffect*> (m_sourceItem->graphicsEffect()); + + if (!effect) { + effect = new ShaderEffect(); + m_sourceItem->setGraphicsEffect(effect); + } + + if (effect) + effect->addRenderTarget(this); + + m_sourceItem->update(); +} + +void ShaderEffectSource::detachSourceItem() +{ + if (!m_sourceItem) + return; + + ShaderEffect* effect = qobject_cast<ShaderEffect*> (m_sourceItem->graphicsEffect()); + + if (effect) + effect->removeRenderTarget(this); + + delete m_fbo; + m_fbo = 0; + + delete m_multisampledFbo; + m_multisampledFbo = 0; + + m_dirtyTexture = true; +} diff --git a/src/imports/shaders/shadereffectsource.h b/src/imports/shaders/shadereffectsource.h new file mode 100644 index 00000000..d7b44bcc --- /dev/null +++ b/src/imports/shaders/shadereffectsource.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHADEREFFECTSOURCE_H +#define SHADEREFFECTSOURCE_H + +#include <QDeclarativeItem> +#include <QtOpenGL> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ShaderEffectBuffer; + +class ShaderEffectSource : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeItem *sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged) + Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged) + Q_PROPERTY(QSize textureSize READ textureSize WRITE setTextureSize NOTIFY textureSizeChanged) + Q_PROPERTY(bool live READ isLive WRITE setLive NOTIFY liveChanged) + Q_PROPERTY(bool hideSource READ hideSource WRITE setHideSource NOTIFY hideSourceChanged) + Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) + Q_ENUMS(WrapMode) + Q_ENUMS(Format) + +public: + enum WrapMode { + ClampToEdge, + RepeatHorizontally, + RepeatVertically, + Repeat + }; + + enum Format { + Alpha = GL_ALPHA, + RGB = GL_RGB, + RGBA = GL_RGBA + }; + + ShaderEffectSource(QDeclarativeItem *parent = 0); + virtual ~ShaderEffectSource(); + + QDeclarativeItem *sourceItem() const { return m_sourceItem.data(); } + void setSourceItem(QDeclarativeItem *item); + + QRectF sourceRect() const { return m_sourceRect; }; + void setSourceRect(const QRectF &rect); + + QSize textureSize() const { return m_textureSize; } + void setTextureSize(const QSize &size); + + bool isLive() const { return m_live; } + void setLive(bool s); + + bool hideSource() const { return m_hideSource; } + void setHideSource(bool hide); + + WrapMode wrapMode() const { return m_wrapMode; }; + void setWrapMode(WrapMode mode); + + bool isActive() const { return m_refs; } + void bind(); + void refFromEffectItem(); + void derefFromEffectItem(); + void updateBackbuffer(); + + ShaderEffectBuffer* fbo() { return m_fbo; } + bool isDirtyTexture() { return m_dirtyTexture; } + bool isMirrored() { return m_mirrored; } + + Q_INVOKABLE void grab(); + +Q_SIGNALS: + void sourceItemChanged(); + void sourceRectChanged(); + void textureSizeChanged(); + void formatChanged(); + void liveChanged(); + void hideSourceChanged(); + void activeChanged(); + void repaintRequired(); + void wrapModeChanged(); + +public Q_SLOTS: + void markSceneGraphDirty(); + void markSourceSizeDirty(); + void markSourceItemDirty(); + +private: + void updateSizeAndTexture(); + void attachSourceItem(); + void detachSourceItem(); + +private: + QPointer<QDeclarativeItem> m_sourceItem; + WrapMode m_wrapMode; + QRectF m_sourceRect; + QSize m_textureSize; + Format m_format; + QSize m_size; + + ShaderEffectBuffer *m_fbo; + ShaderEffectBuffer *m_multisampledFbo; + int m_refs; + bool m_dirtyTexture : 1; + bool m_dirtySceneGraph : 1; + bool m_multisamplingSupported : 1; + bool m_checkedForMultisamplingSupport : 1; + bool m_live : 1; + bool m_hideSource : 1; + bool m_mirrored : 1; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + + +#endif // SHADEREFFECTSOURCE_H diff --git a/src/imports/shaders/shaders.pro b/src/imports/shaders/shaders.pro new file mode 100644 index 00000000..51a9a918 --- /dev/null +++ b/src/imports/shaders/shaders.pro @@ -0,0 +1,38 @@ +TARGET = qmlshadersplugin +TARGETPATH = Qt/labs/shaders +include(../qimportbase.pri) + +QT += declarative opengl + +SOURCES += \ + qmlshadersplugin_plugin.cpp \ + shadereffect.cpp \ + shadereffectitem.cpp \ + shadereffectsource.cpp \ + scenegraph/qsggeometry.cpp \ + shadereffectbuffer.cpp + +HEADERS += \ + qmlshadersplugin_plugin.h \ + glfunctions.h \ + shadereffect.h \ + shadereffectitem.h \ + shadereffectsource.h \ + scenegraph/qsggeometry.h \ + shadereffectbuffer.h + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +symbian:{ + TARGET.UID3 = 0x20034907 + isEmpty(DESTDIR):importFiles.sources = qmlparticlesplugin$${QT_LIBINFIX}.dll qmldir + else:importFiles.sources = $$DESTDIR/qmlparticlesplugin$${QT_LIBINFIX}.dll qmldir + importFiles.path = $$QT_IMPORTS_BASE_DIR/$$TARGETPATH + DEPLOYMENT += importFiles +} + +INSTALLS += target qmldir |