From ea49b368749a2b85c8303af394504abd31ef1c62 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Mon, 19 Apr 2010 15:03:02 +0000 Subject: [PATCH] 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 --- CMakeLists.txt | 1 + private/effects/ripple.cpp | 210 +++++++++++++++++++++++++++++++++++++ private/effects/ripple_p.h | 132 +++++++++++++++++++++++ 3 files changed, 343 insertions(+) create mode 100644 private/effects/ripple.cpp create mode 100644 private/effects/ripple_p.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ca3d4787..3e88e5294 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/private/effects/ripple.cpp b/private/effects/ripple.cpp new file mode 100644 index 000000000..faf60fb8c --- /dev/null +++ b/private/effects/ripple.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2010 Bruno Abinader + * + * 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 . + */ + +#include +#include + +#include + +#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 diff --git a/private/effects/ripple_p.h b/private/effects/ripple_p.h new file mode 100644 index 000000000..967d40068 --- /dev/null +++ b/private/effects/ripple_p.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010 Bruno Abinader + * + * 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 . + */ + +#ifndef PLASMA_EFFECTS_RIPPLE_P_H +#define PLASMA_EFFECTS_RIPPLE_P_H + +#include + +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