aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/particles
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/quick/particles
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/quick/particles')
-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
4 files changed, 232 insertions, 71 deletions
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