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>
This commit is contained in:
parent
3233e8052d
commit
298b86b95b
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,8 +307,56 @@ void QQuickStochasticEngine::setGoal(int state, int sprite, bool jump)
|
|||
return;
|
||||
}
|
||||
|
||||
QDeclarativePixmap::Status QQuickSpriteEngine::status()//Composed status of all Sprites
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
m_sprites << sprite;
|
||||
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;
|
||||
|
@ -315,25 +365,13 @@ QImage QQuickSpriteEngine::assembledImage()
|
|||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
|
||||
//qDebug() << "MAX TEXTURE SIZE" << maxSize;
|
||||
foreach (QQuickStochasticState* s, m_states){
|
||||
QQuickSprite* sprite = qobject_cast<QQuickSprite*>(s);
|
||||
if (sprite)
|
||||
m_sprites << sprite;
|
||||
else
|
||||
qDebug() << "Error: Non-sprite in QQuickSpriteEngine";
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
m_opacitytable_name = table;
|
||||
}
|
||||
|
||||
if (!m_opacityTable)
|
||||
m_opacityTable = new ImageData;
|
||||
if (table == m_opacityTable->source)
|
||||
return;
|
||||
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: ";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++){
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue