Initial ripple effect implementation

This ripple effect will be used by the WaterAnimation to generate a cool simulation of water flowing inside a plasma widget :)

svn path=/trunk/KDE/kdelibs/; revision=1116473
This commit is contained in:
Bruno de Oliveira Abinader 2010-04-19 15:03:02 +00:00
parent 1d8d908d9b
commit ea49b36874
3 changed files with 343 additions and 0 deletions

View File

@ -115,6 +115,7 @@ set(plasma_LIB_SRCS
private/windowpreview.cpp
private/kineticscroll.cpp
private/effects/halopainter.cpp
private/effects/ripple.cpp
querymatch.cpp
remote/accessmanager.cpp
remote/accessappletjob.cpp

210
private/effects/ripple.cpp Normal file
View File

@ -0,0 +1,210 @@
/*
* Copyright (C) 2010 Bruno Abinader <bruno.abinader@indt.org.br>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QtCore/QObject>
#include <QtGui/QPainter>
#include <KDebug>
#include "ripple_p.h"
/**
* Allocates an integer matrix with the given size.
* @param size Matrix size
* @return integer matrix
*/
int **allocateWaveMap(const QSize &size)
{
int **waveMap = new int *[size.width()];
if (!waveMap) {
kDebug() << "could not allocate wave map";
return 0;
}
for (int x = 0; x < size.width(); ++x) {
waveMap[x] = new int[size.height()];
if (!waveMap[x]) {
kDebug() << "could not allocate wave map";
return 0;
}
}
return waveMap;
}
/**
* Deallocates an integer matrix
* @param waveMap integer matrix
*/
void deallocateWaveMap(int **waveMap)
{
if (waveMap) {
delete [] *waveMap;
delete [] waveMap;
}
}
namespace Plasma
{
RippleEffect::RippleEffect(QObject *parent)
: QGraphicsEffect(parent),
m_offset(1),
m_damping(16),
m_heigth(1),
m_opacity(0.0),
m_mapSize(boundingRect().size().toSize()),
m_previousMap(0),
m_currentMap(0)
{
}
RippleEffect::~RippleEffect()
{
deallocateWaveMap(m_previousMap);
deallocateWaveMap(m_currentMap);
}
qint8 RippleEffect::offset() const
{
return m_offset;
}
qint8 RippleEffect::damping() const
{
return m_damping;
}
qint8 RippleEffect::heigth() const
{
return m_heigth;
}
qreal RippleEffect::opacity() const
{
return m_opacity;
}
void RippleEffect::setOffset(qint8 offset)
{
m_offset = offset;
emit offsetChanged(m_offset);
}
void RippleEffect::setDamping(qint8 damping)
{
m_damping = damping;
emit dampingChanged(m_damping);
}
void RippleEffect::setHeigth(qint8 heigth)
{
m_heigth = heigth;
emit heigthChanged(m_heigth);
}
void RippleEffect::setOpacity(qreal opacity)
{
m_opacity = opacity;
update();
}
void RippleEffect::draw(QPainter *painter)
{
QPoint offset;
const QImage currentImage = sourcePixmap(Qt::LogicalCoordinates, &offset).toImage();
QImage modifiedImage = currentImage;
if (!m_previousMap && !m_currentMap) {
m_previousMap = allocateWaveMap(currentImage.size());
m_currentMap = allocateWaveMap(currentImage.size());
}
qint8 x, y;
if (qFuzzyCompare(m_opacity, 0.0)) {
for (x = 0; x < currentImage.width(); ++x) {
memset(m_currentMap[x], 0, sizeof(int) * currentImage.height());
memset(m_previousMap[x], 0, sizeof(int) * currentImage.height());
}
m_mapSize = currentImage.size();
int waveLength = m_mapSize.width() > m_mapSize.height() ? m_mapSize.width() : m_mapSize.height();
m_currentMap[m_mapSize.width() >> 1][m_mapSize.height() >> 1] = waveLength << m_heigth;
} else if (m_mapSize != currentImage.size()) {
const qreal scaleFactorX = qreal(currentImage.width()) / qreal(m_mapSize.width());
const qreal scaleFactorY = qreal(currentImage.height()) / qreal(m_mapSize.height());
int **newPreviousMap = allocateWaveMap(currentImage.size());
int **newCurrentMap = allocateWaveMap(currentImage.size());
qint8 i, j;
for (y = 0; y < currentImage.height(); ++y) {
for (x = 0; x < currentImage.width(); ++x) {
i = x / scaleFactorX;
j = y / scaleFactorY;
newPreviousMap[x][y] = m_previousMap[i][j];
newCurrentMap[x][y] = m_currentMap[i][j];
}
}
deallocateWaveMap(m_previousMap);
deallocateWaveMap(m_currentMap);
m_mapSize = currentImage.size();
m_previousMap = newPreviousMap;
m_currentMap = newCurrentMap;
}
const int width = m_mapSize.width();
const int height = m_mapSize.height();
int neighbours;
int wave;
int xOffset, yOffset;
for (y = m_offset; y < height - m_offset - 1; ++y) {
for (x = m_offset; x < width - m_offset - 1; ++x) {
neighbours = m_previousMap[x+m_offset][y] +
m_previousMap[x-m_offset][y] +
m_previousMap[x][y+m_offset] +
m_previousMap[x][y-m_offset];
if (!neighbours && !m_currentMap[x][y]) {
continue;
}
wave = (neighbours >> 1) - m_currentMap[x][y];
wave -= wave >> m_damping;
m_currentMap[x][y] = wave;
xOffset = x + m_currentMap[x+m_offset][y] - wave;
yOffset = y + m_currentMap[x][y+m_offset] - wave;
modifiedImage.setPixel(x, y, currentImage.pixel(
qBound(0, xOffset, width - 1),
qBound(0, yOffset, height - 1)));
}
}
// Swap wave maps
int **pointer = m_previousMap;
m_previousMap = m_currentMap;
m_currentMap = pointer;
// Restart wave if image center has no wave
if (m_currentMap[width >> 1][height >> 1] == 0) {
int waveLength = width > height ? width : height;
m_currentMap[width >> 1][height >> 1] = waveLength << m_heigth;
}
painter->drawImage(offset, currentImage);
painter->setOpacity(1 - m_opacity);
painter->drawImage(offset, modifiedImage);
}
} // namespace Plasma

132
private/effects/ripple_p.h Normal file
View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2010 Bruno Abinader <bruno.abinader@indt.org.br>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PLASMA_EFFECTS_RIPPLE_P_H
#define PLASMA_EFFECTS_RIPPLE_P_H
#include <QtGui/QGraphicsEffect>
class QObject;
class QPainter;
namespace Plasma
{
/**
* @class RippleEffect plasma/private/effects/ripple_p.h
* @short Ripple effect.
*
* Simulates a ripple effect on the source. This class can be used to simulate a "water" animation.
*/
class RippleEffect : public QGraphicsEffect
{
Q_OBJECT
Q_PROPERTY(qint8 offset READ offset WRITE setOffset)
Q_PROPERTY(qint8 damping READ damping WRITE setDamping)
Q_PROPERTY(qint8 heigth READ heigth WRITE setHeigth)
Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity)
public:
/**
* Default constructor
*
* @param parent Effect object parent.
*/
RippleEffect(QObject *parent = 0);
/** Destructor */
~RippleEffect();
/**
* Returns the ripple offset. The offset is used to calculate the distande between
* neighbour pixels.
* @return ripple offset
*/
qint8 offset() const;
/** Returns the ripple damping factor. The damping factor is used to reduce the wave height
* through each pass.
* @return ripple damping factor
*/
qint8 damping() const;
/** Returns the ripple wave heigth factor. The heigth factor is used to enlarge or reduce the
* initial wave heigth.
* @return ripple wave heigth factor
*/
qint8 heigth() const;
/** Returns the ripple opacity. The opacity is used to reduce the effect opacity when
* animating.
* @return ripple opacity level
*/
qreal opacity() const;
public slots:
/**
* Set ripple offset (e.g. 1).
*/
void setOffset(qint8 offset);
/**
* Set ripple damping factor (e.g. 16).
*/
void setDamping(qint8 damping);
/**
* Set ripple wave heigth factor (e.g. 1).
*/
void setHeigth(qint8 heigth);
/**
* Set ripple opacity level (e.g. 1.0).
*/
void setOpacity(qreal opacity);
signals:
/**
* Emitted when the ripple offset has changed.
* @param offset the ripple offset
*/
void offsetChanged(qint8 offset);
/**
* Emitted when the ripple damping factor has changed.
* @param damping the ripple damping factor
*/
void dampingChanged(qint8 damping);
/**
* Emitted when the ripple wave heigth factor has changed.
* @param heigth the ripple wave heigth factor
*/
void heigthChanged(qint8 heigth);
protected:
/**
* Reimplemented from QGraphicsEffect::draw().
* @param painter source painter
*/
void draw(QPainter *painter);
private:
Q_DISABLE_COPY(RippleEffect)
qint8 m_offset; /** Ripple offset (default is 1) */
qint8 m_damping; /** Ripple damping factor (default is 16) */
qint8 m_heigth; /** Ripple wave heigth factor (default is 1) */
qreal m_opacity; /** Ripple opacity level (default is 0.0) */
QSize m_mapSize; /** Ripple matrix wave size */
int **m_previousMap; /** Last ripple matrix wave */
int **m_currentMap; /** Current ripple matrix wave */
};
} // namespace Plasma
#endif // PLASMA_EFFECTS_RIPPLE_P_H