aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlan Alpert <alan.alpert@nokia.com>2012-01-25 19:18:25 +1000
committerQt by Nokia <qt-info@nokia.com>2012-02-09 07:32:07 +0100
commit298b86b95bd42d12e15e8d8a137cd9bee21d6094 (patch)
tree6ac8ee1d463f419966d64756452849d36913a885 /src
parent3233e8052d2d25fd36567f67f9cd314cf0eaef92 (diff)
Use QDeclarativePixmap in the Particle System
This allows for source URLs to come from network sources. Change-Id: I416edca010e77e507598eaf4eead4291f044f379 Reviewed-by: Martin Jones <martin.jones@nokia.com>
Diffstat (limited to 'src')
-rw-r--r--src/quick/items/qquicksprite.cpp8
-rw-r--r--src/quick/items/qquicksprite_p.h8
-rw-r--r--src/quick/items/qquickspriteengine.cpp76
-rw-r--r--src/quick/items/qquickspriteengine_p.h18
-rw-r--r--src/quick/items/qquickspriteimage.cpp10
-rw-r--r--src/quick/particles/qquickimageparticle.cpp200
-rw-r--r--src/quick/particles/qquickimageparticle_p.h40
-rw-r--r--src/quick/particles/qquickmaskextruder.cpp46
-rw-r--r--src/quick/particles/qquickmaskextruder_p.h17
9 files changed, 326 insertions, 97 deletions
diff --git a/src/quick/items/qquicksprite.cpp b/src/quick/items/qquicksprite.cpp
index 46d1944dc1..4de7880916 100644
--- a/src/quick/items/qquicksprite.cpp
+++ b/src/quick/items/qquicksprite.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qquicksprite_p.h"
+#include <qdeclarative.h>
#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -255,4 +256,11 @@ int QQuickSprite::variedDuration() const //Deals with precedence when multiple d
return QQuickStochasticState::variedDuration() * m_frames;
}
+void QQuickSprite::startImageLoading()
+{
+ m_pix.clear(this);
+ if (!m_source.isEmpty())
+ m_pix.load(qmlEngine(this), m_source);
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicksprite_p.h b/src/quick/items/qquicksprite_p.h
index bf0a4651f5..4c5e5ff58e 100644
--- a/src/quick/items/qquicksprite_p.h
+++ b/src/quick/items/qquicksprite_p.h
@@ -46,6 +46,7 @@
#include <QUrl>
#include <QVariantMap>
#include <QDeclarativeListProperty>
+#include <QtQuick/private/qdeclarativepixmapcache_p.h>
#include "qquickspriteengine_p.h"
QT_BEGIN_HEADER
@@ -90,7 +91,6 @@ public:
return m_frameWidth;
}
-
bool reverse() const
{
return m_reverse;
@@ -181,6 +181,7 @@ public slots:
if (m_source != arg) {
m_source = arg;
emit sourceChanged(arg);
+ startImageLoading();
}
}
@@ -200,7 +201,6 @@ public slots:
}
}
-
void setReverse(bool arg)
{
if (m_reverse != arg) {
@@ -273,6 +273,9 @@ public slots:
}
}
+private slots:
+ void startImageLoading();
+
private:
friend class QQuickImageParticle;
friend class QQuickSpriteImage;
@@ -295,6 +298,7 @@ private:
int m_frameDuration;
int m_frameDurationVariation;
bool m_frameSync;
+ QDeclarativePixmap m_pix;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index a076a0cc61..c19a5e65c9 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -41,6 +41,8 @@
#include "qquickspriteengine_p.h"
#include "qquicksprite_p.h"
+#include <qdeclarativeinfo.h>
+#include <qdeclarative.h>
#include <QDebug>
#include <QPainter>
#include <QSet>
@@ -100,12 +102,12 @@ QQuickStochasticEngine::~QQuickStochasticEngine()
}
QQuickSpriteEngine::QQuickSpriteEngine(QObject *parent)
- : QQuickStochasticEngine(parent)
+ : QQuickStochasticEngine(parent), m_startedImageAssembly(false)
{
}
QQuickSpriteEngine::QQuickSpriteEngine(QList<QQuickSprite*> sprites, QObject *parent)
- : QQuickStochasticEngine(parent)
+ : QQuickStochasticEngine(parent), m_startedImageAssembly(false)
{
foreach (QQuickSprite* sprite, sprites)
m_states << (QQuickStochasticState*)sprite;
@@ -305,16 +307,35 @@ void QQuickStochasticEngine::setGoal(int state, int sprite, bool jump)
return;
}
-QImage QQuickSpriteEngine::assembledImage()
+QDeclarativePixmap::Status QQuickSpriteEngine::status()//Composed status of all Sprites
{
- int h = 0;
- int w = 0;
- m_maxFrames = 0;
- m_imageStateCount = 0;
- int maxSize = 0;
+ if (!m_startedImageAssembly)
+ return QDeclarativePixmap::Null;
+ int null, loading, ready;
+ null = loading = ready = 0;
+ foreach (QQuickSprite* s, m_sprites) {
+ switch (s->m_pix.status()) {
+ case QDeclarativePixmap::Null : null++; break;
+ case QDeclarativePixmap::Loading : loading++; break;
+ case QDeclarativePixmap::Error : return QDeclarativePixmap::Error;
+ case QDeclarativePixmap::Ready : ready++; break;
+ }
+ }
+ if (null)
+ return QDeclarativePixmap::Null;
+ if (loading)
+ return QDeclarativePixmap::Loading;
+ if (ready)
+ return QDeclarativePixmap::Ready;
+ return QDeclarativePixmap::Null;
+}
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
- //qDebug() << "MAX TEXTURE SIZE" << maxSize;
+void QQuickSpriteEngine::startAssemblingImage()
+{
+ if (m_startedImageAssembly)
+ return;
+
+ //This could also trigger the start of the image loading in Sprites, however that currently happens in Sprite::setSource
foreach (QQuickStochasticState* s, m_states){
QQuickSprite* sprite = qobject_cast<QQuickSprite*>(s);
if (sprite)
@@ -322,18 +343,35 @@ QImage QQuickSpriteEngine::assembledImage()
else
qDebug() << "Error: Non-sprite in QQuickSpriteEngine";
}
+ m_startedImageAssembly = true;
+}
+QImage QQuickSpriteEngine::assembledImage()
+{
+ QDeclarativePixmap::Status stat = status();
+ if (stat == QDeclarativePixmap::Error)
+ foreach (QQuickSprite* s, m_sprites)
+ if (s->m_pix.isError())
+ qmlInfo(s) << s->m_pix.error();
+
+ if (stat != QDeclarativePixmap::Ready)
+ return QImage();
+
+ int h = 0;
+ int w = 0;
+ m_maxFrames = 0;
+ m_imageStateCount = 0;
+ int maxSize = 0;
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
+ //qDebug() << "MAX TEXTURE SIZE" << maxSize;
foreach (QQuickSprite* state, m_sprites){
if (state->frames() > m_maxFrames)
m_maxFrames = state->frames();
- QImage img(state->source().toLocalFile());
- if (img.isNull()) {
- qWarning() << "SpriteEngine: loading image failed..." << state->source().toLocalFile();
- return QImage();
- }
+ QImage img = state->m_pix.image();
- //Check that the frame sizes are the same within one engine
+ //Check that the frame sizes are the same within one sprite
if (!state->m_frameWidth)
state->m_frameWidth = img.width() / state->frames();
@@ -347,10 +385,10 @@ QImage QQuickSpriteEngine::assembledImage()
int rowsNeeded = helper::divRoundUp(state->frames(), (maxSize / state->frameWidth()));
if (h + rowsNeeded * state->frameHeight() > maxSize){
if (rowsNeeded * state->frameHeight() > maxSize)
- qWarning() << "SpriteEngine: Animation too large to fit in one texture:" << state->source().toLocalFile();
+ qmlInfo(state) << "SpriteEngine: Animation too large to fit in one texture:" << state->source().toLocalFile();
else
- qWarning() << "SpriteEngine: Animations too large to fit in one texture, pushed over the edge by:" << state->source().toLocalFile();
- qWarning() << "SpriteEngine: Your texture max size today is " << maxSize;
+ qmlInfo(state) << "SpriteEngine: Animations too large to fit in one texture, pushed over the edge by:" << state->source().toLocalFile();
+ qmlInfo(state) << "SpriteEngine: Your texture max size today is " << maxSize;
}
state->m_generatedCount = rowsNeeded;
h += state->frameHeight() * rowsNeeded;
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index 34de7282ec..500d526da5 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -50,6 +50,7 @@
#include <QDeclarativeListProperty>
#include <QImage>
#include <QPair>
+#include <QtQuick/private/qdeclarativepixmapcache_p.h>
QT_BEGIN_HEADER
@@ -61,7 +62,7 @@ class Q_AUTOTEST_EXPORT QQuickStochasticState : public QObject //Currently for i
Q_OBJECT
Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
Q_PROPERTY(int durationVariation READ durationVariation WRITE setDurationVariation NOTIFY durationVariationChanged)
- //Note than manually advanced sprites need to query this variable and implement own behaviour for it
+ //Note that manually advanced sprites need to query this variable and implement own behaviour for it
Q_PROPERTY(bool randomStart READ randomStart WRITE setRandomStart NOTIFY randomStartChanged)
Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
@@ -268,21 +269,30 @@ public:
int spriteState(int sprite=0);
int spriteStart(int sprite=0);
int spriteFrames(int sprite=0);
- int spriteDuration(int sprite=0);//Full duration, not per frame
+ int spriteDuration(int sprite=0);
int spriteX(int sprite=0);
int spriteY(int sprite=0);
int spriteWidth(int sprite=0);
int spriteHeight(int sprite=0);
int spriteCount();//Like state count
int maxFrames();
- QString realName(int sprite=0);//Gives the parent sprite name for pseudosprites
- QImage assembledImage();
virtual void restart(int index=0);
virtual void advance(int index=0);
+
+ //Similar API to QDeclarativePixmap for async loading convenience
+ bool isNull() { return status() == QDeclarativePixmap::Null; }
+ bool isReady() { return status() == QDeclarativePixmap::Ready; }
+ bool isLoading() { return status() == QDeclarativePixmap::Loading; }
+ bool isError() { return status() == QDeclarativePixmap::Error; }
+ QDeclarativePixmap::Status status();//Composed status of all Sprites
+ void startAssemblingImage();
+ QImage assembledImage();
+
private:
int pseudospriteProgress(int,int,int*rd=0);
QList<QQuickSprite*> m_sprites;
+ bool m_startedImageAssembly;
};
//Common use is to have your own list property which is transparently an engine
diff --git a/src/quick/items/qquickspriteimage.cpp b/src/quick/items/qquickspriteimage.cpp
index 755c41d031..1b3b57710a 100644
--- a/src/quick/items/qquickspriteimage.cpp
+++ b/src/quick/items/qquickspriteimage.cpp
@@ -49,6 +49,7 @@
#include <QtQuick/qsgtexturematerial.h>
#include <QtQuick/qsgtexture.h>
#include <QtQuick/qquickcanvas.h>
+#include <QtDeclarative/qdeclarativeinfo.h>
#include <QFile>
#include <cmath>
#include <qmath.h>
@@ -324,7 +325,14 @@ static QSGGeometry::AttributeSet SpriteImage_AttributeSet =
QSGGeometryNode* QQuickSpriteImage::buildNode()
{
if (!m_spriteEngine) {
- qWarning() << "SpriteImage: No sprite engine...";
+ qmlInfo(this) << "No sprite engine...";
+ return 0;
+ } else if (m_spriteEngine->status() == QDeclarativePixmap::Null) {
+ m_spriteEngine->startAssemblingImage();
+ update();//Schedule another update, where we will check again
+ return 0;
+ } else if (m_spriteEngine->status() == QDeclarativePixmap::Loading) {
+ update();//Schedule another update, where we will check again
return 0;
}
diff --git a/src/quick/particles/qquickimageparticle.cpp b/src/quick/particles/qquickimageparticle.cpp
index 08a1621aab..ea67c7f6ae 100644
--- a/src/quick/particles/qquickimageparticle.cpp
+++ b/src/quick/particles/qquickimageparticle.cpp
@@ -41,6 +41,7 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
+#include <private/qquickitem_p.h>
#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgtexturematerial.h>
#include <QtQuick/qsgtexture.h>
@@ -53,6 +54,7 @@
#include <QtQuick/qsgengine.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <private/qdeclarativeglobal_p.h>
+#include <QtDeclarative/qdeclarativeinfo.h>
#include <cmath>
QT_BEGIN_NAMESPACE
@@ -784,9 +786,19 @@ void fillUniformArrayFromImage(float* array, const QImage& img, int size)
Default is true.
*/
+/*!
+ \qmlproperty Status QtQuick.Particles2::ImageParticle::status
+
+ The status of loading the image.
+*/
+
QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
: QQuickParticlePainter(parent)
+ , m_image(0)
+ , m_colorTable(0)
+ , m_sizeTable(0)
+ , m_opacityTable(0)
, m_color_variation(0.0)
, m_rootNode(0)
, m_material(0)
@@ -813,6 +825,7 @@ QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
, m_lastLevel(Unknown)
, m_debugMode(false)
, m_entryEffect(Fade)
+ , m_buildingNodes(false)
{
setFlag(ItemHasContents);
}
@@ -828,9 +841,19 @@ QDeclarativeListProperty<QQuickSprite> QQuickImageParticle::sprites()
void QQuickImageParticle::setImage(const QUrl &image)
{
- if (image == m_image_name)
+ if (image.isEmpty()){
+ if (m_image) {
+ delete m_image;
+ emit imageChanged();
+ }
return;
- m_image_name = image;
+ }
+
+ if (!m_image)
+ m_image = new ImageData;
+ if (image == m_image->source)
+ return;
+ m_image->source = image;
emit imageChanged();
reset();
}
@@ -838,27 +861,57 @@ void QQuickImageParticle::setImage(const QUrl &image)
void QQuickImageParticle::setColortable(const QUrl &table)
{
- if (table == m_colortable_name)
+ if (table.isEmpty()){
+ if (m_colorTable) {
+ delete m_colorTable;
+ emit colortableChanged();
+ }
return;
- m_colortable_name = table;
+ }
+
+ if (!m_colorTable)
+ m_colorTable = new ImageData;
+ if (table == m_colorTable->source)
+ return;
+ m_colorTable->source = table;
emit colortableChanged();
reset();
}
void QQuickImageParticle::setSizetable(const QUrl &table)
{
- if (table == m_sizetable_name)
+ if (table.isEmpty()){
+ if (m_sizeTable) {
+ delete m_sizeTable;
+ emit sizetableChanged();
+ }
return;
- m_sizetable_name = table;
+ }
+
+ if (!m_sizeTable)
+ m_sizeTable = new ImageData;
+ if (table == m_sizeTable->source)
+ return;
+ m_sizeTable->source = table;
emit sizetableChanged();
reset();
}
void QQuickImageParticle::setOpacitytable(const QUrl &table)
{
- if (table == m_opacitytable_name)
+ if (table.isEmpty()){
+ if (m_opacityTable) {
+ delete m_opacityTable;
+ emit opacitytableChanged();
+ }
+ return;
+ }
+
+ if (!m_opacityTable)
+ m_opacityTable = new ImageData;
+ if (table == m_opacityTable->source)
return;
- m_opacitytable_name = table;
+ m_opacityTable->source = table;
emit opacitytableChanged();
reset();
}
@@ -1199,24 +1252,63 @@ QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datu
return m_shadowData[datum->group][datum->index];
}
-QSGGeometryNode* QQuickImageParticle::buildParticleNodes()
+bool QQuickImageParticle::loadingSomething()
+{
+ return (m_image && m_image->pix.isLoading())
+ || (m_colorTable && m_colorTable->pix.isLoading())
+ || (m_sizeTable && m_sizeTable->pix.isLoading())
+ || (m_opacityTable && m_opacityTable->pix.isLoading())
+ || (m_spriteEngine && m_spriteEngine->isLoading());
+}
+
+void QQuickImageParticle::buildParticleNodes()//Starts async parts, like loading images.
+{
+ if (m_rootNode || loadingSomething())
+ return;
+
+ if (!m_buildingNodes) {
+ if (m_image) {//ImageData created on setSource
+ m_image->pix.clear(this);
+ m_image->pix.load(qmlEngine(this), m_image->source);
+ }
+
+ if (m_spriteEngine)
+ m_spriteEngine->startAssemblingImage();
+
+ if (m_colorTable)
+ m_colorTable->pix.load(qmlEngine(this), m_colorTable->source);
+
+ if (m_sizeTable)
+ m_sizeTable->pix.load(qmlEngine(this), m_sizeTable->source);
+
+ if (m_opacityTable)
+ m_opacityTable->pix.load(qmlEngine(this), m_opacityTable->source);
+
+ m_buildingNodes = true;
+ if (loadingSomething())
+ return;
+ }
+ finishBuildParticleNodes();
+}
+
+void QQuickImageParticle::finishBuildParticleNodes()
{
+ m_buildingNodes = false;
#ifdef QT_OPENGL_ES_2
if (m_count * 4 > 0xffff) {
printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort
- return 0;
+ return;
}
#endif
if (count() <= 0)
- return 0;
+ return;
m_debugMode = m_system->m_debugMode;
if (m_sprites.count() || m_bypassOptimizations) {
perfLevel = Sprites;
- } else if (!m_colortable_name.isEmpty() || !m_sizetable_name.isEmpty()
- || !m_opacitytable_name.isEmpty()) {
+ } else if (m_colorTable || m_sizeTable || m_opacityTable) {
perfLevel = Tabled;
} else if (m_autoRotation || m_rotation || m_rotationVariation
|| m_rotationSpeed || m_rotationSpeedVariation
@@ -1251,27 +1343,6 @@ QSGGeometryNode* QQuickImageParticle::buildParticleNodes()
if (perfLevel >= Colored && !m_color.isValid())
m_color = QColor(Qt::white);//Hidden default, but different from unset
- QImage image;
- if (perfLevel >= Sprites){
- if (!m_spriteEngine) {
- qWarning() << "ImageParticle: No sprite engine...";
- //Sprite performance mode with static image is supported, but not advised
- //Note that in this case it always uses shadow data
- } else {
- image = m_spriteEngine->assembledImage();
- if (image.isNull())//Warning is printed in engine
- return 0;
- }
- }
-
- if ( image.isNull() ) {
- image = QImage(m_image_name.toLocalFile());
- if (image.isNull()) {
- printf("ImageParticle: loading image failed '%s'\n", qPrintable(m_image_name.toLocalFile()));
- return 0;
- }
- }
-
clearShadows();
if (m_material)
m_material = 0;
@@ -1280,23 +1351,55 @@ QSGGeometryNode* QQuickImageParticle::buildParticleNodes()
QImage colortable;
QImage sizetable;
QImage opacitytable;
+ QImage image;
+ bool imageLoaded = false;
switch (perfLevel) {//Fallthrough intended
case Sprites:
+ if (!m_spriteEngine) {
+ qWarning() << "ImageParticle: No sprite engine...";
+ //Sprite performance mode with static image is supported, but not advised
+ //Note that in this case it always uses shadow data
+ } else {
+ image = m_spriteEngine->assembledImage();
+ if (image.isNull())//Warning is printed in engine
+ return;
+ imageLoaded = true;
+ }
m_material = SpriteMaterial::createMaterial();
+ if (imageLoaded)
+ getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
if (m_spriteEngine)
m_spriteEngine->setCount(m_count);
case Tabled:
if (!m_material)
m_material = TabledMaterial::createMaterial();
- colortable = QImage(m_colortable_name.toLocalFile());
- sizetable = QImage(m_sizetable_name.toLocalFile());
- opacitytable = QImage(m_opacitytable_name.toLocalFile());
- if (colortable.isNull()){
+
+ if (m_colorTable) {
+ if (m_colorTable->pix.isReady())
+ colortable = m_colorTable->pix.image();
+ else
+ qmlInfo(this) << "Error loading color table: " << m_colorTable->pix.error();
+ }
+
+ if (m_sizeTable) {
+ if (m_sizeTable->pix.isReady())
+ sizetable = m_sizeTable->pix.image();
+ else
+ qmlInfo(this) << "Error loading size table: " << m_sizeTable->pix.error();
+ }
+
+ if (m_opacityTable) {
+ if (m_opacityTable->pix.isReady())
+ opacitytable = m_opacityTable->pix.image();
+ else
+ qmlInfo(this) << "Error loading opacity table: " << m_opacityTable->pix.error();
+ }
+
+ if (colortable.isNull()){//###Goes through image just for this
colortable = QImage(1,1,QImage::Format_ARGB32);
colortable.fill(Qt::white);
}
- Q_ASSERT(!colortable.isNull());
getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
@@ -1309,7 +1412,16 @@ QSGGeometryNode* QQuickImageParticle::buildParticleNodes()
default://Also Simple
if (!m_material)
m_material = SimpleMaterial::createMaterial();
- getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
+ if (!imageLoaded) {
+ if (!m_image->pix.isReady()) {
+ qmlInfo(this) << m_image->pix.error();
+ delete m_material;
+ return;
+ }
+ //getState<ImageMaterialData>(m_material)->texture //TODO: Shouldn't this be better? But not crash?
+ // = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory());
+ getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(m_image->pix.image());
+ }
getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
m_material->setFlag(QSGMaterial::Blending);
@@ -1390,7 +1502,8 @@ QSGGeometryNode* QQuickImageParticle::buildParticleNodes()
(*(m_nodes.begin()))->appendChildNode(node);
}
- return *(m_nodes.begin());
+ m_rootNode = *(m_nodes.begin());
+ update();
}
QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
@@ -1409,6 +1522,7 @@ QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
m_material = 0;
m_pleaseReset = false;
+ m_buildingNodes = false;//Cancel a part-way build
}
if (m_system && m_system->isRunning() && !m_system->isPaused()){
@@ -1417,6 +1531,8 @@ QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
update();
foreach (QSGGeometryNode* node, m_nodes)
node->markDirty(QSGNode::DirtyGeometry);
+ } else if (m_buildingNodes) {
+ update();//To call prepareNextFrame() again from the renderThread
}
}
@@ -1426,7 +1542,7 @@ QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
void QQuickImageParticle::prepareNextFrame()
{
if (m_rootNode == 0){//TODO: Staggered loading (as emitted)
- m_rootNode = buildParticleNodes();
+ buildParticleNodes();
if (m_debugMode) {
qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
qDebug() << "QQuickImageParticle Nodes: ";
diff --git a/src/quick/particles/qquickimageparticle_p.h b/src/quick/particles/qquickimageparticle_p.h
index e42332bee9..a0f1595192 100644
--- a/src/quick/particles/qquickimageparticle_p.h
+++ b/src/quick/particles/qquickimageparticle_p.h
@@ -43,6 +43,7 @@
#define ULTRAPARTICLE_H
#include "qquickparticlepainter_p.h"
#include "qquickdirection_p.h"
+#include <private/qdeclarativepixmapcache_p.h>
#include <QDeclarativeListProperty>
#include <QtQuick/qsgsimplematerial.h>
#include <QtGui/qcolor.h>
@@ -149,6 +150,11 @@ class QQuickImageParticle : public QQuickParticlePainter
{
Q_OBJECT
Q_PROPERTY(QUrl source READ image WRITE setImage NOTIFY imageChanged)
+ Q_PROPERTY(QDeclarativeListProperty<QQuickSprite> sprites READ sprites)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+ //### Is it worth having progress like Image has?
+ //Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
+
Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged)
Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged)
Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged)
@@ -172,20 +178,20 @@ class QQuickImageParticle : public QQuickParticlePainter
//to 180 will lead to facing away from the direction of motion
Q_PROPERTY(bool autoRotation READ autoRotation WRITE setAutoRotation NOTIFY autoRotationChanged RESET resetRotation)
- //###Call i/j? Makes more sense to those with vector calculus experience, and I could even add the cirumflex in QML?
//xVector is the vector from the top-left point to the top-right point, and is multiplied by current size
Q_PROPERTY(QQuickDirection* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged RESET resetDeformation)
//yVector is the same, but top-left to bottom-left. The particle is always a parallelogram.
Q_PROPERTY(QQuickDirection* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged RESET resetDeformation)
- Q_PROPERTY(QDeclarativeListProperty<QQuickSprite> sprites READ sprites)
Q_PROPERTY(bool spritesInterpolate READ spritesInterpolate WRITE setSpritesInterpolate NOTIFY spritesInterpolateChanged)
Q_PROPERTY(EntryEffect entryEffect READ entryEffect WRITE setEntryEffect NOTIFY entryEffectChanged)
Q_ENUMS(EntryEffect)
+ Q_ENUMS(Status)
public:
explicit QQuickImageParticle(QQuickItem *parent = 0);
virtual ~QQuickImageParticle();
+ enum Status { Null, Ready, Loading, Error };
QDeclarativeListProperty<QQuickSprite> sprites();
QQuickStochasticEngine* spriteEngine() {return m_spriteEngine;}
@@ -205,16 +211,16 @@ public:
Sprites
};
- QUrl image() const { return m_image_name; }
+ QUrl image() const { return m_image ? m_image->source : QUrl(); }
void setImage(const QUrl &image);
- QUrl colortable() const { return m_colortable_name; }
+ QUrl colortable() const { return m_colorTable ? m_colorTable->source : QUrl(); }
void setColortable(const QUrl &table);
- QUrl sizetable() const { return m_sizetable_name; }
+ QUrl sizetable() const { return m_sizeTable ? m_sizeTable->source : QUrl(); }
void setSizetable (const QUrl &table);
- QUrl opacitytable() const { return m_opacitytable_name; }
+ QUrl opacitytable() const { return m_opacityTable ? m_opacityTable->source : QUrl(); }
void setOpacitytable(const QUrl &table);
QColor color() const { return m_color; }
@@ -253,6 +259,8 @@ public:
EntryEffect entryEffect() const { return m_entryEffect; }
+ Status status() const { return m_status; }
+
void resetColor();
void resetRotation();
void resetDeformation();
@@ -297,6 +305,8 @@ signals:
void entryEffectChanged(EntryEffect arg);
+ void statusChanged(Status arg);
+
public slots:
void reloadColor(const Color4ub &c, QQuickParticleData* d);
void setAlphaVariation(qreal arg);
@@ -336,18 +346,24 @@ protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
void prepareNextFrame();
- QSGGeometryNode* buildParticleNodes();
+ void buildParticleNodes();
private slots:
void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
void spriteAdvance(int spriteIndex);
void spritesUpdate(qreal time = 0 );
+ void finishBuildParticleNodes();
private:
- QUrl m_image_name;
- QUrl m_colortable_name;
- QUrl m_sizetable_name;
- QUrl m_opacitytable_name;
+ struct ImageData {
+ QUrl source;
+ QDeclarativePixmap pix;
+ };
+ ImageData *m_image;
+ ImageData *m_colorTable;
+ ImageData *m_sizeTable;
+ ImageData *m_opacityTable;
+ bool loadingSomething();
QColor m_color;
@@ -419,6 +435,8 @@ private:
return static_cast<QSGSimpleMaterial<MaterialData> *>(m)->state();
}
EntryEffect m_entryEffect;
+ Status m_status;
+ bool m_buildingNodes;
};
QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickmaskextruder.cpp b/src/quick/particles/qquickmaskextruder.cpp
index 90a664ae50..3ed2eb953c 100644
--- a/src/quick/particles/qquickmaskextruder.cpp
+++ b/src/quick/particles/qquickmaskextruder.cpp
@@ -40,6 +40,8 @@
****************************************************************************/
#include "qquickmaskextruder_p.h"
+#include <QtDeclarative/qdeclarative.h>
+#include <QtDeclarative/qdeclarativeinfo.h>
#include <QImage>
#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -65,6 +67,36 @@ QQuickMaskExtruder::QQuickMaskExtruder(QObject *parent) :
{
}
+void QQuickMaskExtruder::setSource(QUrl arg)
+{
+ if (m_source != arg) {
+ m_source = arg;
+
+ m_lastHeight = -1;//Trigger reset
+ m_lastWidth = -1;
+ emit sourceChanged(arg);
+ startMaskLoading();
+ }
+}
+
+void QQuickMaskExtruder::startMaskLoading()
+{
+ m_pix.clear(this);
+ if (m_source.isEmpty())
+ return;
+ m_pix.load(qmlEngine(this), m_source);
+ if (m_pix.isLoading())
+ m_pix.connectFinished(this, SLOT(finishMaskLoading()));
+ else
+ finishMaskLoading();
+}
+
+void QQuickMaskExtruder::finishMaskLoading()
+{
+ if (m_pix.isError())
+ qmlInfo(this) << m_pix.error();
+}
+
QPointF QQuickMaskExtruder::extrude(const QRectF &r)
{
ensureInitialized(r);
@@ -88,19 +120,15 @@ void QQuickMaskExtruder::ensureInitialized(const QRectF &r)
{
if (m_lastWidth == r.width() && m_lastHeight == r.height())
return;//Same as before
+ if (!m_pix.isReady())
+ return;
m_lastWidth = r.width();
m_lastHeight = r.height();
- m_img = QImage();
m_mask.clear();
- if (m_source.isEmpty())
- return;
- m_img = QImage(m_source.toLocalFile());
- if (m_img.isNull()){
- qWarning() << "MaskShape: Cannot load" << qPrintable(m_source.toLocalFile());
- return;
- }
- m_img = m_img.createAlphaMask();
+
+ m_img = m_pix.image().createAlphaMask();
+ m_pix.clear();
m_img = m_img.convertToFormat(QImage::Format_Mono);//Else LSB, but I think that's easier
m_img = m_img.scaled(r.size().toSize());//TODO: Do they need aspect ratio stuff? Or tiling?
for (int i=0; i<r.width(); i++){
diff --git a/src/quick/particles/qquickmaskextruder_p.h b/src/quick/particles/qquickmaskextruder_p.h
index 08821406ff..0c9f10b1b2 100644
--- a/src/quick/particles/qquickmaskextruder_p.h
+++ b/src/quick/particles/qquickmaskextruder_p.h
@@ -42,6 +42,7 @@
#ifndef MASKEXTRUDER_H
#define MASKEXTRUDER_H
#include "qquickparticleextruder_p.h"
+#include <private/qdeclarativepixmapcache_p.h>
#include <QUrl>
#include <QImage>
@@ -68,24 +69,22 @@ signals:
void sourceChanged(QUrl arg);
public slots:
+ void setSource(QUrl arg);
+
+private slots:
+ void startMaskLoading();
+ void finishMaskLoading();
- void setSource(QUrl arg)
- {
- if (m_source != arg) {
- m_source = arg;
- m_lastHeight = -1;//Trigger reset
- m_lastWidth = -1;
- emit sourceChanged(arg);
- }
- }
private:
QUrl m_source;
void ensureInitialized(const QRectF &r);
int m_lastWidth;
int m_lastHeight;
+ QDeclarativePixmap m_pix;
QImage m_img;
QList<QPointF> m_mask;//TODO: More memory efficient datastructures
+ //Perhaps just the mask for the largest bounds is stored, and interpolate up
};
QT_END_NAMESPACE