From 298b86b95bd42d12e15e8d8a137cd9bee21d6094 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 25 Jan 2012 19:18:25 +1000 Subject: Use QDeclarativePixmap in the Particle System This allows for source URLs to come from network sources. Change-Id: I416edca010e77e507598eaf4eead4291f044f379 Reviewed-by: Martin Jones --- src/quick/items/qquicksprite.cpp | 8 ++ src/quick/items/qquicksprite_p.h | 8 +- src/quick/items/qquickspriteengine.cpp | 76 ++++++++--- src/quick/items/qquickspriteengine_p.h | 18 ++- src/quick/items/qquickspriteimage.cpp | 10 +- src/quick/particles/qquickimageparticle.cpp | 200 ++++++++++++++++++++++------ src/quick/particles/qquickimageparticle_p.h | 40 ++++-- src/quick/particles/qquickmaskextruder.cpp | 46 +++++-- src/quick/particles/qquickmaskextruder_p.h | 17 ++- 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 #include 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 #include #include +#include #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 +#include #include #include #include @@ -100,12 +102,12 @@ QQuickStochasticEngine::~QQuickStochasticEngine() } QQuickSpriteEngine::QQuickSpriteEngine(QObject *parent) - : QQuickStochasticEngine(parent) + : QQuickStochasticEngine(parent), m_startedImageAssembly(false) { } QQuickSpriteEngine::QQuickSpriteEngine(QList 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(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 #include #include +#include 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 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 #include #include +#include #include #include #include @@ -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 #include +#include #include #include #include @@ -53,6 +54,7 @@ #include #include #include +#include #include 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 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(m_material)->texture = QSGPlainTexture::fromImage(image); getState(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(m_material)->colorTable = QSGPlainTexture::fromImage(colortable); fillUniformArrayFromImage(getState(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE); fillUniformArrayFromImage(getState(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(m_material)->texture = QSGPlainTexture::fromImage(image); + if (!imageLoaded) { + if (!m_image->pix.isReady()) { + qmlInfo(this) << m_image->pix.error(); + delete m_material; + return; + } + //getState(m_material)->texture //TODO: Shouldn't this be better? But not crash? + // = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory()); + getState(m_material)->texture = QSGPlainTexture::fromImage(m_image->pix.image()); + } getState(m_material)->texture->setFiltering(QSGTexture::Linear); getState(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 #include #include #include @@ -149,6 +150,11 @@ class QQuickImageParticle : public QQuickParticlePainter { Q_OBJECT Q_PROPERTY(QUrl source READ image WRITE setImage NOTIFY imageChanged) + Q_PROPERTY(QDeclarativeListProperty 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 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 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 *>(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 +#include #include #include 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 #include #include @@ -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 m_mask;//TODO: More memory efficient datastructures + //Perhaps just the mask for the largest bounds is stored, and interpolate up }; QT_END_NAMESPACE -- cgit v1.2.3