diff options
Diffstat (limited to 'src/widgets/effects')
-rw-r--r-- | src/widgets/effects/effects.pri | 6 | ||||
-rw-r--r-- | src/widgets/effects/qgraphicseffect.cpp | 1235 | ||||
-rw-r--r-- | src/widgets/effects/qgraphicseffect.h | 289 | ||||
-rw-r--r-- | src/widgets/effects/qgraphicseffect_p.h | 231 | ||||
-rw-r--r-- | src/widgets/effects/qpixmapfilter.cpp | 1381 | ||||
-rw-r--r-- | src/widgets/effects/qpixmapfilter_p.h | 196 |
6 files changed, 3338 insertions, 0 deletions
diff --git a/src/widgets/effects/effects.pri b/src/widgets/effects/effects.pri new file mode 100644 index 0000000000..a21f941866 --- /dev/null +++ b/src/widgets/effects/effects.pri @@ -0,0 +1,6 @@ +HEADERS += effects/qgraphicseffect.h \ + effects/qgraphicseffect_p.h \ + effects/qpixmapfilter_p.h + +SOURCES += effects/qgraphicseffect.cpp \ + effects/qpixmapfilter.cpp diff --git a/src/widgets/effects/qgraphicseffect.cpp b/src/widgets/effects/qgraphicseffect.cpp new file mode 100644 index 0000000000..5df29cd297 --- /dev/null +++ b/src/widgets/effects/qgraphicseffect.cpp @@ -0,0 +1,1235 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QGraphicsEffect + \brief The QGraphicsEffect class is the base class for all graphics + effects. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + + Effects alter the appearance of elements by hooking into the rendering + pipeline and operating between the source (e.g., a QGraphicsPixmapItem) + and the destination device (e.g., QGraphicsView's viewport). Effects can be + disabled by calling setEnabled(false). If effects are disabled, the source + is rendered directly. + + To add a visual effect to a QGraphicsItem, for example, you can use one of + the standard effects, or alternately, create your own effect by creating a + subclass of QGraphicsEffect. The effect can then be installed on the item + using QGraphicsItem::setGraphicsEffect(). + + Qt provides the following standard effects: + + \list + \o QGraphicsBlurEffect - blurs the item by a given radius + \o QGraphicsDropShadowEffect - renders a dropshadow behind the item + \o QGraphicsColorizeEffect - renders the item in shades of any given color + \o QGraphicsOpacityEffect - renders the item with an opacity + \endlist + + \table + \row + \o{2,1} \img graphicseffect-plain.png + \row + \o \img graphicseffect-blur.png + \o \img graphicseffect-colorize.png + \row + \o \img graphicseffect-opacity.png + \o \img graphicseffect-drop-shadow.png + \endtable + + \img graphicseffect-widget.png + + For more information on how to use each effect, refer to the specific + effect's documentation. + + To create your own custom effect, create a subclass of QGraphicsEffect (or + any other existing effects) and reimplement the virtual function draw(). + This function is called whenever the effect needs to redraw. The draw() + function takes the painter with which to draw as an argument. For more + information, refer to the documenation for draw(). In the draw() function + you can call sourcePixmap() to get a pixmap of the graphics effect source + which you can then process. + + If your effect changes, use update() to request for a redraw. If your + custom effect changes the bounding rectangle of the source, e.g., a radial + glow effect may need to apply an extra margin, you can reimplement the + virtual boundingRectFor() function, and call updateBoundingRect() + to notify the framework whenever this rectangle changes. The virtual + sourceChanged() function is called to notify the effects that + the source has changed in some way - e.g., if the source is a + QGraphicsRectItem and its rectangle parameters have changed. + + \sa QGraphicsItem::setGraphicsEffect(), QWidget::setGraphicsEffect() +*/ + +#include "qgraphicseffect_p.h" +#include "private/qgraphicsitem_p.h" + +#include <QtWidgets/qgraphicsitem.h> + +#include <QtGui/qimage.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpaintengine.h> +#include <QtCore/qrect.h> +#include <QtCore/qdebug.h> +#include <private/qdrawhelper_p.h> + +#ifndef QT_NO_GRAPHICSEFFECT +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QGraphicsEffectSource + \brief The QGraphicsEffectSource class represents the source on which a + QGraphicsEffect is installed on. + + When a QGraphicsEffect is installed on a QGraphicsItem, for example, this + class will act as a wrapper around QGraphicsItem. Then, calling update() is + effectively the same as calling QGraphicsItem::update(). + + QGraphicsEffectSource also provides a pixmap() function which creates a + pixmap with the source painted into it. + + \sa QGraphicsItem::setGraphicsEffect(), QWidget::setGraphicsEffect(). +*/ + +/*! + \internal +*/ +QGraphicsEffectSource::QGraphicsEffectSource(QGraphicsEffectSourcePrivate &dd, QObject *parent) + : QObject(dd, parent) +{} + +/*! + Destroys the effect source. +*/ +QGraphicsEffectSource::~QGraphicsEffectSource() +{} + +/*! + Returns the bounding rectangle of the source mapped to the given \a system. + + \sa draw() +*/ +QRectF QGraphicsEffectSource::boundingRect(Qt::CoordinateSystem system) const +{ + return d_func()->boundingRect(system); +} + +/*! + Returns the bounding rectangle of the source mapped to the given \a system. + + Calling this function with Qt::DeviceCoordinates outside of + QGraphicsEffect::draw() will give undefined results, as there is no device + context available. + + \sa draw() +*/ +QRectF QGraphicsEffect::sourceBoundingRect(Qt::CoordinateSystem system) const +{ + Q_D(const QGraphicsEffect); + if (d->source) + return d->source->boundingRect(system); + return QRectF(); +} + +/*! + Returns a pointer to the item if this source is a QGraphicsItem; otherwise + returns 0. + + \sa widget() +*/ +const QGraphicsItem *QGraphicsEffectSource::graphicsItem() const +{ + return d_func()->graphicsItem(); +} + +/*! + Returns a pointer to the widget if this source is a QWidget; otherwise + returns 0. + + \sa graphicsItem() +*/ +const QWidget *QGraphicsEffectSource::widget() const +{ + return d_func()->widget(); +} + +/*! + Returns a pointer to the style options (used when drawing the source) if + available; otherwise returns 0. + + \sa graphicsItem(), widget() +*/ +const QStyleOption *QGraphicsEffectSource::styleOption() const +{ + return d_func()->styleOption(); +} + +/*! + Draws the source using the given \a painter. + + This function should only be called from QGraphicsEffect::draw(). + + \sa QGraphicsEffect::draw() +*/ +void QGraphicsEffectSource::draw(QPainter *painter) +{ + Q_D(const QGraphicsEffectSource); + + QPixmap pm; + if (QPixmapCache::find(d->m_cacheKey, &pm)) { + QTransform restoreTransform; + if (d->m_cachedSystem == Qt::DeviceCoordinates) { + restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + } + + painter->drawPixmap(d->m_cachedOffset, pm); + + if (d->m_cachedSystem == Qt::DeviceCoordinates) + painter->setWorldTransform(restoreTransform); + } else { + d_func()->draw(painter); + } +} + +/*! + Draws the source directly using the given \a painter. + + This function should only be called from QGraphicsEffect::draw(). + + For example: + + \snippet doc/src/snippets/code/src_gui_effects_qgraphicseffect.cpp 0 + + \sa QGraphicsEffect::draw() +*/ +void QGraphicsEffect::drawSource(QPainter *painter) +{ + Q_D(const QGraphicsEffect); + if (d->source) + d->source->draw(painter); +} + +/*! + Schedules a redraw of the source. Call this function whenever the source + needs to be redrawn. + + \sa QGraphicsEffect::updateBoundingRect(), QWidget::update(), + QGraphicsItem::update(), +*/ +void QGraphicsEffectSource::update() +{ + d_func()->update(); +} + +/*! + Returns true if the source effectively is a pixmap, e.g., a + QGraphicsPixmapItem. + + This function is useful for optimization purposes. For instance, there's no + point in drawing the source in device coordinates to avoid pixmap scaling + if this function returns true - the source pixmap will be scaled anyways. +*/ +bool QGraphicsEffectSource::isPixmap() const +{ + return d_func()->isPixmap(); +} + +/*! + Returns true if the source effectively is a pixmap, e.g., a + QGraphicsPixmapItem. + + This function is useful for optimization purposes. For instance, there's no + point in drawing the source in device coordinates to avoid pixmap scaling + if this function returns true - the source pixmap will be scaled anyways. +*/ +bool QGraphicsEffect::sourceIsPixmap() const +{ + return source() ? source()->isPixmap() : false; +} + +/*! + Returns a pixmap with the source painted into it. + + The \a system specifies which coordinate system to be used for the source. + The optional \a offset parameter returns the offset where the pixmap should + be painted at using the current painter. + + The \a mode determines how much of the effect the pixmap will contain. + By default, the pixmap will contain the whole effect. + + The returned pixmap is bound to the current painter's device rectangle when + \a system is Qt::DeviceCoordinates. + + \sa QGraphicsEffect::draw(), boundingRect() +*/ +QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offset, QGraphicsEffect::PixmapPadMode mode) const +{ + Q_D(const QGraphicsEffectSource); + + // Shortcut, no cache for childless pixmap items... + const QGraphicsItem *item = graphicsItem(); + if (system == Qt::LogicalCoordinates && mode == QGraphicsEffect::NoPad && item && isPixmap()) { + const QGraphicsPixmapItem *pixmapItem = static_cast<const QGraphicsPixmapItem *>(item); + if (offset) + *offset = pixmapItem->offset().toPoint(); + return pixmapItem->pixmap(); + } + + if (system == Qt::DeviceCoordinates && item + && !static_cast<const QGraphicsItemEffectSourcePrivate *>(d_func())->info) { + qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context"); + return QPixmap(); + } + + QPixmap pm; + if (item && d->m_cachedSystem == system && d->m_cachedMode == mode) + QPixmapCache::find(d->m_cacheKey, &pm); + + if (pm.isNull()) { + pm = d->pixmap(system, &d->m_cachedOffset, mode); + d->m_cachedSystem = system; + d->m_cachedMode = mode; + + d->invalidateCache(); + d->m_cacheKey = QPixmapCache::insert(pm); + } + + if (offset) + *offset = d->m_cachedOffset; + + return pm; +} + +/*! + Returns a pixmap with the source painted into it. + + The \a system specifies which coordinate system to be used for the source. + The optional \a offset parameter returns the offset where the pixmap should + be painted at using the current painter. For control on how the pixmap is + padded use the \a mode parameter. + + The returned pixmap is clipped to the current painter's device rectangle when + \a system is Qt::DeviceCoordinates. + + Calling this function with Qt::DeviceCoordinates outside of + QGraphicsEffect::draw() will give undefined results, as there is no device + context available. + + \sa draw(), boundingRect() +*/ +QPixmap QGraphicsEffect::sourcePixmap(Qt::CoordinateSystem system, QPoint *offset, QGraphicsEffect::PixmapPadMode mode) const +{ + Q_D(const QGraphicsEffect); + if (d->source) + return d->source->pixmap(system, offset, mode); + return QPixmap(); +} + +QGraphicsEffectSourcePrivate::~QGraphicsEffectSourcePrivate() +{ + invalidateCache(); +} + +void QGraphicsEffectSourcePrivate::setCachedOffset(const QPoint &offset) +{ + m_cachedOffset = offset; +} + +void QGraphicsEffectSourcePrivate::invalidateCache(InvalidateReason reason) const +{ + if (m_cachedMode != QGraphicsEffect::PadToEffectiveBoundingRect + && (reason == EffectRectChanged + || (reason == TransformChanged && m_cachedSystem == Qt::LogicalCoordinates))) { + return; + } + + QPixmapCache::remove(m_cacheKey); +} + +/*! + Constructs a new QGraphicsEffect instance having the + specified \a parent. +*/ +QGraphicsEffect::QGraphicsEffect(QObject *parent) + : QObject(*new QGraphicsEffectPrivate, parent) +{ +} + +/*! + \internal +*/ +QGraphicsEffect::QGraphicsEffect(QGraphicsEffectPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Removes the effect from the source, and destroys the graphics effect. +*/ +QGraphicsEffect::~QGraphicsEffect() +{ + Q_D(QGraphicsEffect); + d->setGraphicsEffectSource(0); +} + +/*! + Returns the effective bounding rectangle for this effect, i.e., the + bounding rectangle of the source in device coordinates, adjusted by + any margins applied by the effect itself. + + \sa boundingRectFor(), updateBoundingRect() +*/ +QRectF QGraphicsEffect::boundingRect() const +{ + Q_D(const QGraphicsEffect); + if (d->source) + return boundingRectFor(d->source->boundingRect()); + return QRectF(); +} + +/*! + Returns the effective bounding rectangle for this effect, given the + provided \a rect in the device coordinates. When writing + you own custom effect, you must call updateBoundingRect() whenever any + parameters are changed that may cause this this function to return a + different value. + + \sa sourceBoundingRect() +*/ +QRectF QGraphicsEffect::boundingRectFor(const QRectF &rect) const +{ + return rect; +} + +/*! + \property QGraphicsEffect::enabled + \brief whether the effect is enabled or not. + + If an effect is disabled, the source will be rendered with as normal, with + no interference from the effect. If the effect is enabled, the source will + be rendered with the effect applied. + + This property is enabled by default. + + Using this property, you can disable certain effects on slow platforms, in + order to ensure that the user interface is responsive. +*/ +bool QGraphicsEffect::isEnabled() const +{ + Q_D(const QGraphicsEffect); + return d->isEnabled; +} + +void QGraphicsEffect::setEnabled(bool enable) +{ + Q_D(QGraphicsEffect); + if (d->isEnabled == enable) + return; + + d->isEnabled = enable; + if (d->source) { + d->source->d_func()->effectBoundingRectChanged(); + d->source->d_func()->invalidateCache(); + } + emit enabledChanged(enable); +} + +/*! + \fn void QGraphicsEffect::enabledChanged(bool enabled) + + This signal is emitted whenever the effect is enabled or disabled. + The \a enabled parameter holds the effects's new enabled state. + + \sa isEnabled() +*/ + +/*! + Schedules a redraw of the effect. Call this function whenever the effect + needs to be redrawn. This function does not trigger a redraw of the source. + + \sa updateBoundingRect() +*/ +void QGraphicsEffect::update() +{ + Q_D(QGraphicsEffect); + if (d->source) + d->source->update(); +} + +/*! + \internal + + Returns a pointer to the source, which provides extra context information + that can be useful for the effect. + + \sa draw() +*/ +QGraphicsEffectSource *QGraphicsEffect::source() const +{ + Q_D(const QGraphicsEffect); + return d->source; +} + +/*! + This function notifies the effect framework when the effect's bounding + rectangle has changed. As a custom effect author, you must call this + function whenever you change any parameters that will cause the virtual + boundingRectFor() function to return a different value. + + This function will call update() if this is necessary. + + \sa boundingRectFor(), boundingRect(), sourceBoundingRect() +*/ +void QGraphicsEffect::updateBoundingRect() +{ + Q_D(QGraphicsEffect); + if (d->source) { + d->source->d_func()->effectBoundingRectChanged(); + d->source->d_func()->invalidateCache(QGraphicsEffectSourcePrivate::EffectRectChanged); + } +} + +/*! + \fn virtual void QGraphicsEffect::draw(QPainter *painter) = 0 + + This pure virtual function draws the effect and is called whenever the + source needs to be drawn. + + Reimplement this function in a QGraphicsEffect subclass to provide the + effect's drawing implementation, using \a painter. + + For example: + + \snippet doc/src/snippets/code/src_gui_effects_qgraphicseffect.cpp 1 + + This function should not be called explicitly by the user, since it is + meant for reimplementation purposes only. +*/ + +/*! + \enum QGraphicsEffect::ChangeFlag + + This enum describes what has changed in QGraphicsEffectSource. + + \value SourceAttached The effect is installed on a source. + \value SourceDetached The effect is uninstalled on a source. + \value SourceBoundingRectChanged The bounding rect of the source has + changed. + \value SourceInvalidated The visual appearance of the source has changed. +*/ + +/*! + \enum QGraphicsEffect::PixmapPadMode + + This enum describes how the pixmap returned from sourcePixmap should be + padded. + + \value NoPad The pixmap should not receive any additional + padding. + \value PadToTransparentBorder The pixmap should be padded + to ensure it has a completely transparent border. + \value PadToEffectiveBoundingRect The pixmap should be padded to + match the effective bounding rectangle of the effect. +*/ + +/*! + This virtual function is called by QGraphicsEffect to notify the effect + that the source has changed. If the effect applies any cache, then this + cache must be purged in order to reflect the new appearance of the source. + + The \a flags describes what has changed. +*/ +void QGraphicsEffect::sourceChanged(ChangeFlags flags) +{ + Q_UNUSED(flags); +} + +/*! + \class QGraphicsColorizeEffect + \brief The QGraphicsColorizeEffect class provides a colorize effect. + \since 4.6 + + A colorize effect renders the source with a tint of its color(). The color + can be modified using the setColor() function. + + By default, the color is light blue (QColor(0, 0, 192)). + + \img graphicseffect-colorize.png + + \sa QGraphicsDropShadowEffect, QGraphicsBlurEffect, QGraphicsOpacityEffect +*/ + +/*! + Constructs a new QGraphicsColorizeEffect instance. + The \a parent parameter is passed to QGraphicsEffect's constructor. +*/ +QGraphicsColorizeEffect::QGraphicsColorizeEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsColorizeEffectPrivate, parent) +{ +} + +/*! + Destroys the effect. +*/ +QGraphicsColorizeEffect::~QGraphicsColorizeEffect() +{ +} + +/*! + \property QGraphicsColorizeEffect::color + \brief the color of the effect. + + By default, the color is light blue (QColor(0, 0, 192)). +*/ +QColor QGraphicsColorizeEffect::color() const +{ + Q_D(const QGraphicsColorizeEffect); + return d->filter->color(); +} + +void QGraphicsColorizeEffect::setColor(const QColor &color) +{ + Q_D(QGraphicsColorizeEffect); + if (d->filter->color() == color) + return; + + d->filter->setColor(color); + update(); + emit colorChanged(color); +} + +/*! + \property QGraphicsColorizeEffect::strength + \brief the strength of the effect. + + By default, the strength is 1.0. + A strength 0.0 equals to no effect, while 1.0 means full colorization. +*/ +qreal QGraphicsColorizeEffect::strength() const +{ + Q_D(const QGraphicsColorizeEffect); + return d->filter->strength(); +} + +void QGraphicsColorizeEffect::setStrength(qreal strength) +{ + Q_D(QGraphicsColorizeEffect); + if (qFuzzyCompare(d->filter->strength(), strength)) + return; + + d->filter->setStrength(strength); + d->opaque = !qFuzzyIsNull(strength); + update(); + emit strengthChanged(strength); +} + +/*! \fn void QGraphicsColorizeEffect::strengthChanged(qreal strength) + This signal is emitted whenever setStrength() changes the colorize + strength property. \a strength contains the new strength value of + the colorize effect. + */ + +/*! + \fn void QGraphicsColorizeEffect::colorChanged(const QColor &color) + + This signal is emitted whenever the effect's color changes. + The \a color parameter holds the effect's new color. +*/ + +/*! + \reimp +*/ +void QGraphicsColorizeEffect::draw(QPainter *painter) +{ + Q_D(QGraphicsColorizeEffect); + + if (!d->opaque) { + drawSource(painter); + return; + } + + QPoint offset; + if (sourceIsPixmap()) { + // No point in drawing in device coordinates (pixmap will be scaled anyways). + const QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, NoPad); + if (!pixmap.isNull()) + d->filter->draw(painter, offset, pixmap); + + return; + } + + // Draw pixmap in deviceCoordinates to avoid pixmap scaling. + const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset); + if (pixmap.isNull()) + return; + + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + d->filter->draw(painter, offset, pixmap); + painter->setWorldTransform(restoreTransform); +} + +/*! + \class QGraphicsBlurEffect + \brief The QGraphicsBlurEffect class provides a blur effect. + \since 4.6 + + A blur effect blurs the source. This effect is useful for reducing details, + such as when the source loses focus and you want to draw attention to other + elements. The level of detail can be modified using the setBlurRadius() + function. Use setBlurHints() to choose the blur hints. + + By default, the blur radius is 5 pixels. The blur radius is specified in + device coordinates. + + \img graphicseffect-blur.png + + \sa QGraphicsDropShadowEffect, QGraphicsColorizeEffect, QGraphicsOpacityEffect +*/ + +/*! + \enum QGraphicsBlurEffect::BlurHint + \since 4.6 + + This enum describes the possible hints that can be used to control how + blur effects are applied. The hints might not have an effect in all the + paint engines. + + \value PerformanceHint Indicates that rendering performance is the most important factor, + at the potential cost of lower quality. + + \value QualityHint Indicates that rendering quality is the most important factor, + at the potential cost of lower performance. + + \value AnimationHint Indicates that the blur radius is going to be animated, hinting + that the implementation can keep a cache of blurred verisons of the source. + Do not use this hint if the source is going to be dynamically changing. + + \sa blurHints(), setBlurHints() +*/ + + +/*! + Constructs a new QGraphicsBlurEffect instance. + The \a parent parameter is passed to QGraphicsEffect's constructor. +*/ +QGraphicsBlurEffect::QGraphicsBlurEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsBlurEffectPrivate, parent) +{ + Q_D(QGraphicsBlurEffect); + d->filter->setBlurHints(QGraphicsBlurEffect::PerformanceHint); +} + +/*! + Destroys the effect. +*/ +QGraphicsBlurEffect::~QGraphicsBlurEffect() +{ +} + +/*! + \property QGraphicsBlurEffect::blurRadius + \brief the blur radius of the effect. + + Using a smaller radius results in a sharper appearance, whereas a bigger + radius results in a more blurred appearance. + + By default, the blur radius is 5 pixels. + + The radius is given in device coordinates, meaning it is + unaffected by scale. +*/ +qreal QGraphicsBlurEffect::blurRadius() const +{ + Q_D(const QGraphicsBlurEffect); + return d->filter->radius(); +} + +void QGraphicsBlurEffect::setBlurRadius(qreal radius) +{ + Q_D(QGraphicsBlurEffect); + if (qFuzzyCompare(d->filter->radius(), radius)) + return; + + d->filter->setRadius(radius); + updateBoundingRect(); + emit blurRadiusChanged(radius); +} + +/*! + \fn void QGraphicsBlurEffect::blurRadiusChanged(qreal radius) + + This signal is emitted whenever the effect's blur radius changes. + The \a radius parameter holds the effect's new blur radius. +*/ + +/*! + \property QGraphicsBlurEffect::blurHints + \brief the blur hint of the effect. + + Use the PerformanceHint hint to say that you want a faster blur, + the QualityHint hint to say that you prefer a higher quality blur, + or the AnimationHint when you want to animate the blur radius. + + By default, the blur hint is PerformanceHint. +*/ +QGraphicsBlurEffect::BlurHints QGraphicsBlurEffect::blurHints() const +{ + Q_D(const QGraphicsBlurEffect); + return d->filter->blurHints(); +} + +void QGraphicsBlurEffect::setBlurHints(QGraphicsBlurEffect::BlurHints hints) +{ + Q_D(QGraphicsBlurEffect); + if (d->filter->blurHints() == hints) + return; + + d->filter->setBlurHints(hints); + emit blurHintsChanged(hints); +} + +/*! + \fn void QGraphicsBlurEffect::blurHintsChanged(QGraphicsBlurEffect::BlurHints hints) + + This signal is emitted whenever the effect's blur hints changes. + The \a hints parameter holds the effect's new blur hints. +*/ + +/*! + \reimp +*/ +QRectF QGraphicsBlurEffect::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QGraphicsBlurEffect); + return d->filter->boundingRectFor(rect); +} + +/*! + \reimp +*/ +void QGraphicsBlurEffect::draw(QPainter *painter) +{ + Q_D(QGraphicsBlurEffect); + if (d->filter->radius() < 1) { + drawSource(painter); + return; + } + + PixmapPadMode mode = PadToEffectiveBoundingRect; + if (painter->paintEngine()->type() == QPaintEngine::OpenGL2) + mode = NoPad; + + QPoint offset; + QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, mode); + if (pixmap.isNull()) + return; + + d->filter->draw(painter, offset, pixmap); +} + +/*! + \class QGraphicsDropShadowEffect + \brief The QGraphicsDropShadowEffect class provides a drop shadow effect. + \since 4.6 + + A drop shadow effect renders the source with a drop shadow. The color of + the drop shadow can be modified using the setColor() function. The drop + shadow offset can be modified using the setOffset() function and the blur + radius of the drop shadow can be changed with the setBlurRadius() + function. + + By default, the drop shadow is a semi-transparent dark gray + (QColor(63, 63, 63, 180)) shadow, blurred with a radius of 1 at an offset + of 8 pixels towards the lower right. The drop shadow offset is specified + in device coordinates. + + \img graphicseffect-drop-shadow.png + + \sa QGraphicsBlurEffect, QGraphicsColorizeEffect, QGraphicsOpacityEffect +*/ + +/*! + Constructs a new QGraphicsDropShadowEffect instance. + The \a parent parameter is passed to QGraphicsEffect's constructor. +*/ +QGraphicsDropShadowEffect::QGraphicsDropShadowEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsDropShadowEffectPrivate, parent) +{ +} + +/*! + Destroys the effect. +*/ +QGraphicsDropShadowEffect::~QGraphicsDropShadowEffect() +{ +} + +/*! + \property QGraphicsDropShadowEffect::offset + \brief the shadow offset in pixels. + + By default, the offset is 8 pixels towards the lower right. + + The offset is given in device coordinates, which means it is + unaffected by scale. + + \sa xOffset(), yOffset(), blurRadius(), color() +*/ +QPointF QGraphicsDropShadowEffect::offset() const +{ + Q_D(const QGraphicsDropShadowEffect); + return d->filter->offset(); +} + +void QGraphicsDropShadowEffect::setOffset(const QPointF &offset) +{ + Q_D(QGraphicsDropShadowEffect); + if (d->filter->offset() == offset) + return; + + d->filter->setOffset(offset); + updateBoundingRect(); + emit offsetChanged(offset); +} + +/*! + \property QGraphicsDropShadowEffect::xOffset + \brief the horizontal shadow offset in pixels. + + By default, the horizontal shadow offset is 8 pixels. + + + + \sa yOffset(), offset() +*/ + +/*! + \property QGraphicsDropShadowEffect::yOffset + \brief the vertical shadow offset in pixels. + + By default, the vertical shadow offset is 8 pixels. + + \sa xOffset(), offset() +*/ + +/*! + \fn void QGraphicsDropShadowEffect::offsetChanged(const QPointF &offset) + + This signal is emitted whenever the effect's shadow offset changes. + The \a offset parameter holds the effect's new shadow offset. +*/ + +/*! + \property QGraphicsDropShadowEffect::blurRadius + \brief the blur radius in pixels of the drop shadow. + + Using a smaller radius results in a sharper shadow, whereas using a bigger + radius results in a more blurred shadow. + + By default, the blur radius is 1 pixel. + + \sa color(), offset(). +*/ +qreal QGraphicsDropShadowEffect::blurRadius() const +{ + Q_D(const QGraphicsDropShadowEffect); + return d->filter->blurRadius(); +} + +void QGraphicsDropShadowEffect::setBlurRadius(qreal blurRadius) +{ + Q_D(QGraphicsDropShadowEffect); + if (qFuzzyCompare(d->filter->blurRadius(), blurRadius)) + return; + + d->filter->setBlurRadius(blurRadius); + updateBoundingRect(); + emit blurRadiusChanged(blurRadius); +} + +/*! + \fn void QGraphicsDropShadowEffect::blurRadiusChanged(qreal blurRadius) + + This signal is emitted whenever the effect's blur radius changes. + The \a blurRadius parameter holds the effect's new blur radius. +*/ + +/*! + \property QGraphicsDropShadowEffect::color + \brief the color of the drop shadow. + + By default, the drop color is a semi-transparent dark gray + (QColor(63, 63, 63, 180)). + + \sa offset(), blurRadius() +*/ +QColor QGraphicsDropShadowEffect::color() const +{ + Q_D(const QGraphicsDropShadowEffect); + return d->filter->color(); +} + +void QGraphicsDropShadowEffect::setColor(const QColor &color) +{ + Q_D(QGraphicsDropShadowEffect); + if (d->filter->color() == color) + return; + + d->filter->setColor(color); + update(); + emit colorChanged(color); +} + +/*! + \fn void QGraphicsDropShadowEffect::colorChanged(const QColor &color) + + This signal is emitted whenever the effect's color changes. + The \a color parameter holds the effect's new color. +*/ + +/*! + \reimp +*/ +QRectF QGraphicsDropShadowEffect::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QGraphicsDropShadowEffect); + return d->filter->boundingRectFor(rect); +} + +/*! + \reimp +*/ +void QGraphicsDropShadowEffect::draw(QPainter *painter) +{ + Q_D(QGraphicsDropShadowEffect); + if (d->filter->blurRadius() <= 0 && d->filter->offset().isNull()) { + drawSource(painter); + return; + } + + PixmapPadMode mode = PadToEffectiveBoundingRect; + if (painter->paintEngine()->type() == QPaintEngine::OpenGL2) + mode = NoPad; + + // Draw pixmap in device coordinates to avoid pixmap scaling. + QPoint offset; + const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, mode); + if (pixmap.isNull()) + return; + + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + d->filter->draw(painter, offset, pixmap); + painter->setWorldTransform(restoreTransform); +} + +/*! + \class QGraphicsOpacityEffect + \brief The QGraphicsOpacityEffect class provides an opacity effect. + \since 4.6 + + An opacity effect renders the source with an opacity. This effect is useful + for making the source semi-transparent, similar to a fade-in/fade-out + sequence. The opacity can be modified using the setOpacity() function. + + By default, the opacity is 0.7. + + \img graphicseffect-opacity.png + + \sa QGraphicsDropShadowEffect, QGraphicsBlurEffect, QGraphicsColorizeEffect +*/ + +/*! + Constructs a new QGraphicsOpacityEffect instance. + The \a parent parameter is passed to QGraphicsEffect's constructor. +*/ +QGraphicsOpacityEffect::QGraphicsOpacityEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsOpacityEffectPrivate, parent) +{ +} + +/*! + Destroys the effect. +*/ +QGraphicsOpacityEffect::~QGraphicsOpacityEffect() +{ +} + +/*! + \property QGraphicsOpacityEffect::opacity + \brief the opacity of the effect. + + The value should be in the range of 0.0 to 1.0, where 0.0 is + fully transparent and 1.0 is fully opaque. + + By default, the opacity is 0.7. + + \sa setOpacityMask() +*/ +qreal QGraphicsOpacityEffect::opacity() const +{ + Q_D(const QGraphicsOpacityEffect); + return d->opacity; +} + +void QGraphicsOpacityEffect::setOpacity(qreal opacity) +{ + Q_D(QGraphicsOpacityEffect); + opacity = qBound(qreal(0.0), opacity, qreal(1.0)); + + if (qFuzzyCompare(d->opacity, opacity)) + return; + + d->opacity = opacity; + if ((d->isFullyTransparent = qFuzzyIsNull(d->opacity))) + d->isFullyOpaque = 0; + else + d->isFullyOpaque = qFuzzyIsNull(d->opacity - 1); + update(); + emit opacityChanged(opacity); +} + +/*! + \fn void QGraphicsOpacityEffect::opacityChanged(qreal opacity) + + This signal is emitted whenever the effect's opacity changes. + The \a opacity parameter holds the effect's new opacity. +*/ + +/*! + \property QGraphicsOpacityEffect::opacityMask + \brief the opacity mask of the effect. + + An opacity mask allows you apply opacity to portions of an element. + + For example: + + \snippet doc/src/snippets/code/src_gui_effects_qgraphicseffect.cpp 2 + + There is no opacity mask by default. + + \sa setOpacity() +*/ +QBrush QGraphicsOpacityEffect::opacityMask() const +{ + Q_D(const QGraphicsOpacityEffect); + return d->opacityMask; +} + +void QGraphicsOpacityEffect::setOpacityMask(const QBrush &mask) +{ + Q_D(QGraphicsOpacityEffect); + if (d->opacityMask == mask) + return; + + d->opacityMask = mask; + d->hasOpacityMask = (mask.style() != Qt::NoBrush); + update(); + + emit opacityMaskChanged(mask); +} + +/*! + \fn void QGraphicsOpacityEffect::opacityMaskChanged(const QBrush &mask) + + This signal is emitted whenever the effect's opacity mask changes. + The \a mask parameter holds the effect's new opacity mask. +*/ + +/*! + \reimp +*/ +void QGraphicsOpacityEffect::draw(QPainter *painter) +{ + Q_D(QGraphicsOpacityEffect); + + // Transparent; nothing to draw. + if (d->isFullyTransparent) + return; + + // Opaque; draw directly without going through a pixmap. + if (d->isFullyOpaque && !d->hasOpacityMask) { + drawSource(painter); + return; + } + + QPoint offset; + Qt::CoordinateSystem system = sourceIsPixmap() ? Qt::LogicalCoordinates : Qt::DeviceCoordinates; + QPixmap pixmap = sourcePixmap(system, &offset, QGraphicsEffect::NoPad); + if (pixmap.isNull()) + return; + + painter->save(); + painter->setOpacity(d->opacity); + + if (d->hasOpacityMask) { + QPainter pixmapPainter(&pixmap); + pixmapPainter.setRenderHints(painter->renderHints()); + pixmapPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + if (system == Qt::DeviceCoordinates) { + QTransform worldTransform = painter->worldTransform(); + worldTransform *= QTransform::fromTranslate(-offset.x(), -offset.y()); + pixmapPainter.setWorldTransform(worldTransform); + pixmapPainter.fillRect(sourceBoundingRect(), d->opacityMask); + } else { + pixmapPainter.translate(-offset); + pixmapPainter.fillRect(pixmap.rect(), d->opacityMask); + } + } + + if (system == Qt::DeviceCoordinates) + painter->setWorldTransform(QTransform()); + + painter->drawPixmap(offset, pixmap); + painter->restore(); +} + + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSEFFECT diff --git a/src/widgets/effects/qgraphicseffect.h b/src/widgets/effects/qgraphicseffect.h new file mode 100644 index 0000000000..0ee6b26463 --- /dev/null +++ b/src/widgets/effects/qgraphicseffect.h @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSEFFECT_H +#define QGRAPHICSEFFECT_H + +#include <QtCore/qobject.h> +#include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtGui/qcolor.h> +#include <QtGui/qbrush.h> + +#ifndef QT_NO_GRAPHICSEFFECT +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGraphicsItem; +class QStyleOption; +class QPainter; +class QPixmap; + +class QGraphicsEffectSource; + +class QGraphicsEffectPrivate; +class Q_WIDGETS_EXPORT QGraphicsEffect : public QObject +{ + Q_OBJECT + Q_FLAGS(ChangeFlags) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) +public: + enum ChangeFlag { + SourceAttached = 0x1, + SourceDetached = 0x2, + SourceBoundingRectChanged = 0x4, + SourceInvalidated = 0x8 + }; + Q_DECLARE_FLAGS(ChangeFlags, ChangeFlag) + + enum PixmapPadMode { + NoPad, + PadToTransparentBorder, + PadToEffectiveBoundingRect + }; + + QGraphicsEffect(QObject *parent = 0); + virtual ~QGraphicsEffect(); + + virtual QRectF boundingRectFor(const QRectF &sourceRect) const; + QRectF boundingRect() const; + + bool isEnabled() const; + +public Q_SLOTS: + void setEnabled(bool enable); + void update(); + +Q_SIGNALS: + void enabledChanged(bool enabled); + +protected: + QGraphicsEffect(QGraphicsEffectPrivate &d, QObject *parent = 0); + virtual void draw(QPainter *painter) = 0; + virtual void sourceChanged(ChangeFlags flags); + void updateBoundingRect(); + + bool sourceIsPixmap() const; + QRectF sourceBoundingRect(Qt::CoordinateSystem system = Qt::LogicalCoordinates) const; + void drawSource(QPainter *painter); + QPixmap sourcePixmap(Qt::CoordinateSystem system = Qt::LogicalCoordinates, + QPoint *offset = 0, + PixmapPadMode mode = PadToEffectiveBoundingRect) const; + +private: + Q_DECLARE_PRIVATE(QGraphicsEffect) + Q_DISABLE_COPY(QGraphicsEffect) + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; + friend class QGraphicsScenePrivate; + friend class QWidget; + friend class QWidgetPrivate; + +public: + QGraphicsEffectSource *source() const; // internal + +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsEffect::ChangeFlags) + +class QGraphicsColorizeEffectPrivate; +class Q_WIDGETS_EXPORT QGraphicsColorizeEffect: public QGraphicsEffect +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged) +public: + QGraphicsColorizeEffect(QObject *parent = 0); + ~QGraphicsColorizeEffect(); + + QColor color() const; + qreal strength() const; + +public Q_SLOTS: + void setColor(const QColor &c); + void setStrength(qreal strength); + +Q_SIGNALS: + void colorChanged(const QColor &color); + void strengthChanged(qreal strength); + +protected: + void draw(QPainter *painter); + +private: + Q_DECLARE_PRIVATE(QGraphicsColorizeEffect) + Q_DISABLE_COPY(QGraphicsColorizeEffect) +}; + +class QGraphicsBlurEffectPrivate; +class Q_WIDGETS_EXPORT QGraphicsBlurEffect: public QGraphicsEffect +{ + Q_OBJECT + Q_FLAGS(BlurHint BlurHints) + Q_PROPERTY(qreal blurRadius READ blurRadius WRITE setBlurRadius NOTIFY blurRadiusChanged) + Q_PROPERTY(BlurHints blurHints READ blurHints WRITE setBlurHints NOTIFY blurHintsChanged) +public: + enum BlurHint { + PerformanceHint = 0x00, + QualityHint = 0x01, + AnimationHint = 0x02 + }; + Q_DECLARE_FLAGS(BlurHints, BlurHint) + + QGraphicsBlurEffect(QObject *parent = 0); + ~QGraphicsBlurEffect(); + + QRectF boundingRectFor(const QRectF &rect) const; + qreal blurRadius() const; + BlurHints blurHints() const; + +public Q_SLOTS: + void setBlurRadius(qreal blurRadius); + void setBlurHints(BlurHints hints); + +Q_SIGNALS: + void blurRadiusChanged(qreal blurRadius); + void blurHintsChanged(BlurHints hints); + +protected: + void draw(QPainter *painter); + +private: + Q_DECLARE_PRIVATE(QGraphicsBlurEffect) + Q_DISABLE_COPY(QGraphicsBlurEffect) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsBlurEffect::BlurHints) + +class QGraphicsDropShadowEffectPrivate; +class Q_WIDGETS_EXPORT QGraphicsDropShadowEffect: public QGraphicsEffect +{ + Q_OBJECT + Q_PROPERTY(QPointF offset READ offset WRITE setOffset NOTIFY offsetChanged) + Q_PROPERTY(qreal xOffset READ xOffset WRITE setXOffset NOTIFY offsetChanged) + Q_PROPERTY(qreal yOffset READ yOffset WRITE setYOffset NOTIFY offsetChanged) + Q_PROPERTY(qreal blurRadius READ blurRadius WRITE setBlurRadius NOTIFY blurRadiusChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) +public: + QGraphicsDropShadowEffect(QObject *parent = 0); + ~QGraphicsDropShadowEffect(); + + QRectF boundingRectFor(const QRectF &rect) const; + QPointF offset() const; + + inline qreal xOffset() const + { return offset().x(); } + + inline qreal yOffset() const + { return offset().y(); } + + qreal blurRadius() const; + QColor color() const; + +public Q_SLOTS: + void setOffset(const QPointF &ofs); + + inline void setOffset(qreal dx, qreal dy) + { setOffset(QPointF(dx, dy)); } + + inline void setOffset(qreal d) + { setOffset(QPointF(d, d)); } + + inline void setXOffset(qreal dx) + { setOffset(QPointF(dx, yOffset())); } + + inline void setYOffset(qreal dy) + { setOffset(QPointF(xOffset(), dy)); } + + void setBlurRadius(qreal blurRadius); + void setColor(const QColor &color); + +Q_SIGNALS: + void offsetChanged(const QPointF &offset); + void blurRadiusChanged(qreal blurRadius); + void colorChanged(const QColor &color); + +protected: + void draw(QPainter *painter); + +private: + Q_DECLARE_PRIVATE(QGraphicsDropShadowEffect) + Q_DISABLE_COPY(QGraphicsDropShadowEffect) +}; + +class QGraphicsOpacityEffectPrivate; +class Q_WIDGETS_EXPORT QGraphicsOpacityEffect: public QGraphicsEffect +{ + Q_OBJECT + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged) + Q_PROPERTY(QBrush opacityMask READ opacityMask WRITE setOpacityMask NOTIFY opacityMaskChanged) +public: + QGraphicsOpacityEffect(QObject *parent = 0); + ~QGraphicsOpacityEffect(); + + qreal opacity() const; + QBrush opacityMask() const; + +public Q_SLOTS: + void setOpacity(qreal opacity); + void setOpacityMask(const QBrush &mask); + +Q_SIGNALS: + void opacityChanged(qreal opacity); + void opacityMaskChanged(const QBrush &mask); + +protected: + void draw(QPainter *painter); + +private: + Q_DECLARE_PRIVATE(QGraphicsOpacityEffect) + Q_DISABLE_COPY(QGraphicsOpacityEffect) +}; + +QT_END_NAMESPACE + +QT_END_HEADER +#endif //QT_NO_GRAPHICSEFFECT + +#endif // QGRAPHICSEFFECT_H + diff --git a/src/widgets/effects/qgraphicseffect_p.h b/src/widgets/effects/qgraphicseffect_p.h new file mode 100644 index 0000000000..ac15f04d05 --- /dev/null +++ b/src/widgets/effects/qgraphicseffect_p.h @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSEFFECT_P_H +#define QGRAPHICSEFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicseffect.h" + +#include <QPixmapCache> + +#include <private/qobject_p.h> +#include <private/qpixmapfilter_p.h> + +#ifndef QT_NO_GRAPHICSEFFECT +QT_BEGIN_NAMESPACE + +class QGraphicsEffectSourcePrivate; +class Q_WIDGETS_EXPORT QGraphicsEffectSource : public QObject +{ + Q_OBJECT +public: + ~QGraphicsEffectSource(); + const QGraphicsItem *graphicsItem() const; + const QWidget *widget() const; + const QStyleOption *styleOption() const; + + bool isPixmap() const; + void draw(QPainter *painter); + void update(); + + QRectF boundingRect(Qt::CoordinateSystem coordinateSystem = Qt::LogicalCoordinates) const; + QRect deviceRect() const; + QPixmap pixmap(Qt::CoordinateSystem system = Qt::LogicalCoordinates, + QPoint *offset = 0, + QGraphicsEffect::PixmapPadMode mode = QGraphicsEffect::PadToEffectiveBoundingRect) const; + +protected: + QGraphicsEffectSource(QGraphicsEffectSourcePrivate &dd, QObject *parent = 0); + +private: + Q_DECLARE_PRIVATE(QGraphicsEffectSource) + Q_DISABLE_COPY(QGraphicsEffectSource) + friend class QGraphicsEffect; + friend class QGraphicsEffectPrivate; + friend class QGraphicsScenePrivate; + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; + friend class QWidget; + friend class QWidgetPrivate; +}; + +class QGraphicsEffectSourcePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsEffectSource) +public: + QGraphicsEffectSourcePrivate() + : QObjectPrivate() + , m_cachedSystem(Qt::DeviceCoordinates) + , m_cachedMode(QGraphicsEffect::PadToTransparentBorder) + {} + + enum InvalidateReason + { + TransformChanged, + EffectRectChanged, + SourceChanged + }; + + virtual ~QGraphicsEffectSourcePrivate(); + virtual void detach() = 0; + virtual QRectF boundingRect(Qt::CoordinateSystem system) const = 0; + virtual QRect deviceRect() const = 0; + virtual const QGraphicsItem *graphicsItem() const = 0; + virtual const QWidget *widget() const = 0; + virtual const QStyleOption *styleOption() const = 0; + virtual void draw(QPainter *p) = 0; + virtual void update() = 0; + virtual bool isPixmap() const = 0; + virtual QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset = 0, + QGraphicsEffect::PixmapPadMode mode = QGraphicsEffect::PadToTransparentBorder) const = 0; + virtual void effectBoundingRectChanged() = 0; + + void setCachedOffset(const QPoint &offset); + void invalidateCache(InvalidateReason reason = SourceChanged) const; + Qt::CoordinateSystem currentCachedSystem() const { return m_cachedSystem; } + QGraphicsEffect::PixmapPadMode currentCachedMode() const { return m_cachedMode; } + + friend class QGraphicsScenePrivate; + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; + +private: + mutable Qt::CoordinateSystem m_cachedSystem; + mutable QGraphicsEffect::PixmapPadMode m_cachedMode; + mutable QPoint m_cachedOffset; + mutable QPixmapCache::Key m_cacheKey; +}; + +class Q_WIDGETS_EXPORT QGraphicsEffectPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsEffect) +public: + QGraphicsEffectPrivate() : source(0), isEnabled(1) {} + + inline void setGraphicsEffectSource(QGraphicsEffectSource *newSource) + { + QGraphicsEffect::ChangeFlags flags; + if (source) { + flags |= QGraphicsEffect::SourceDetached; + source->d_func()->invalidateCache(); + source->d_func()->detach(); + delete source; + } + source = newSource; + if (newSource) + flags |= QGraphicsEffect::SourceAttached; + q_func()->sourceChanged(flags); + } + + QGraphicsEffectSource *source; + QRectF boundingRect; + quint32 isEnabled : 1; + quint32 padding : 31; // feel free to use +}; + + +class QGraphicsColorizeEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsColorizeEffect) +public: + QGraphicsColorizeEffectPrivate() + : opaque(true) + { + filter = new QPixmapColorizeFilter; + } + ~QGraphicsColorizeEffectPrivate() { delete filter; } + + QPixmapColorizeFilter *filter; + quint32 opaque : 1; + quint32 padding : 31; +}; + +class QGraphicsBlurEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsBlurEffect) +public: + QGraphicsBlurEffectPrivate() : filter(new QPixmapBlurFilter) {} + ~QGraphicsBlurEffectPrivate() { delete filter; } + + QPixmapBlurFilter *filter; +}; + +class QGraphicsDropShadowEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsDropShadowEffect) +public: + QGraphicsDropShadowEffectPrivate() : filter(new QPixmapDropShadowFilter) {} + ~QGraphicsDropShadowEffectPrivate() { delete filter; } + + QPixmapDropShadowFilter *filter; +}; + +class QGraphicsOpacityEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsOpacityEffect) +public: + QGraphicsOpacityEffectPrivate() + : opacity(qreal(0.7)), isFullyTransparent(0), isFullyOpaque(0), hasOpacityMask(0) {} + ~QGraphicsOpacityEffectPrivate() {} + + qreal opacity; + QBrush opacityMask; + uint isFullyTransparent : 1; + uint isFullyOpaque : 1; + uint hasOpacityMask : 1; +}; + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSEFFECT +#endif // QGRAPHICSEFFECT_P_H + diff --git a/src/widgets/effects/qpixmapfilter.cpp b/src/widgets/effects/qpixmapfilter.cpp new file mode 100644 index 0000000000..922b45cb61 --- /dev/null +++ b/src/widgets/effects/qpixmapfilter.cpp @@ -0,0 +1,1381 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qglobal.h> + +#include <QDebug> + +#include "qpainter.h" +#include "qpixmap.h" +#include "qpixmapfilter_p.h" +#include "qvarlengtharray.h" + +#include "private/qguiapplication_p.h" +#include "private/qpaintengineex_p.h" +#include "private/qpaintengine_raster_p.h" +#include "qmath.h" +#include "private/qmath_p.h" +#include "private/qmemrotate_p.h" +#include "private/qdrawhelper_p.h" + +#ifndef QT_NO_GRAPHICSEFFECT +QT_BEGIN_NAMESPACE + +class QPixmapFilterPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QPixmapFilter) +public: + QPixmapFilter::FilterType type; +}; + +/*! + \class QPixmapFilter + \since 4.5 + \ingroup painting + + \brief The QPixmapFilter class provides the basic functionality for + pixmap filter classes. Pixmap filter can be for example colorize or blur. + + QPixmapFilter is the base class for every pixmap filter. QPixmapFilter is + an abstract class and cannot itself be instantiated. It provides a standard + interface for filter processing. + + \internal +*/ + +/*! + \enum QPixmapFilter::FilterType + + \internal + + This enum describes the types of filter that can be applied to pixmaps. + + \value ConvolutionFilter A filter that is used to calculate the convolution + of the image with a kernel. See + QPixmapConvolutionFilter for more information. + \value ColorizeFilter A filter that is used to change the overall color + of an image. See QPixmapColorizeFilter for more + information. + \value DropShadowFilter A filter that is used to add a drop shadow to an + image. See QPixmapDropShadowFilter for more + information. + \value BlurFilter A filter that is used to blur an image using + a simple blur radius. See QPixmapBlurFilter + for more information. + + \value UserFilter The first filter type that can be used for + application-specific purposes. +*/ + + +/*! + Constructs a default QPixmapFilter with the given \a type. + + This constructor should be used when subclassing QPixmapFilter to + create custom user filters. + + \internal +*/ +QPixmapFilter::QPixmapFilter(FilterType type, QObject *parent) + : QObject(*new QPixmapFilterPrivate, parent) +{ + d_func()->type = type; +} + + + +/*! + \internal +*/ +QPixmapFilter::QPixmapFilter(QPixmapFilterPrivate&d, QPixmapFilter::FilterType type, QObject *parent) + : QObject(d, parent) +{ + d_func()->type = type; +} + + +/*! + Destroys the pixmap filter. + + \internal +*/ +QPixmapFilter::~QPixmapFilter() +{ +} + +/*! + Returns the type of the filter. All standard pixmap filter classes + are associated with a unique value. + + \internal +*/ +QPixmapFilter::FilterType QPixmapFilter::type() const +{ + Q_D(const QPixmapFilter); + return d->type; +} + +/*! + Returns the bounding rectangle that is affected by the pixmap + filter if the filter is applied to the specified \a rect. + + \internal +*/ +QRectF QPixmapFilter::boundingRectFor(const QRectF &rect) const +{ + return rect; +} + +/*! + \fn void QPixmapFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const + + Uses \a painter to draw filtered result of \a src at the point + specified by \a p. If \a srcRect is specified the it will + be used as a source rectangle to only draw a part of the source. + + draw() will affect the area which boundingRectFor() returns. + + \internal +*/ + +/*! + \class QPixmapConvolutionFilter + \since 4.5 + \ingroup painting + + \brief The QPixmapConvolutionFilter class provides convolution + filtering for pixmaps. + + QPixmapConvolutionFilter implements a convolution pixmap filter, + which is applied when \l{QPixmapFilter::}{draw()} is called. A + convolution filter lets you distort an image by setting the values + of a matrix of qreal values called its + \l{setConvolutionKernel()}{kernel}. The matrix's values are + usually between -1.0 and 1.0. + + \omit + In convolution filtering, the pixel value is calculated from the + neighboring pixels based on the weighting convolution kernel. + This needs explaining to be useful. + \endomit + + Example: + \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 1 + + \sa {Pixmap Filters Example}, QPixmapColorizeFilter, QPixmapDropShadowFilter + + + \internal +*/ + +class QPixmapConvolutionFilterPrivate : public QPixmapFilterPrivate +{ +public: + QPixmapConvolutionFilterPrivate(): convolutionKernel(0), kernelWidth(0), kernelHeight(0), convoluteAlpha(false) {} + ~QPixmapConvolutionFilterPrivate() { + delete[] convolutionKernel; + } + + qreal *convolutionKernel; + int kernelWidth; + int kernelHeight; + bool convoluteAlpha; +}; + + +/*! + Constructs a pixmap convolution filter. + + By default there is no convolution kernel. + + \internal +*/ +QPixmapConvolutionFilter::QPixmapConvolutionFilter(QObject *parent) + : QPixmapFilter(*new QPixmapConvolutionFilterPrivate, ConvolutionFilter, parent) +{ + Q_D(QPixmapConvolutionFilter); + d->convoluteAlpha = true; +} + +/*! + Destructor of pixmap convolution filter. + + \internal +*/ +QPixmapConvolutionFilter::~QPixmapConvolutionFilter() +{ +} + +/*! + Sets convolution kernel with the given number of \a rows and \a columns. + Values from \a kernel are copied to internal data structure. + + To preserve the intensity of the pixmap, the sum of all the + values in the convolution kernel should add up to 1.0. A sum + greater than 1.0 produces a lighter result and a sum less than 1.0 + produces a darker and transparent result. + + \internal +*/ +void QPixmapConvolutionFilter::setConvolutionKernel(const qreal *kernel, int rows, int columns) +{ + Q_D(QPixmapConvolutionFilter); + delete [] d->convolutionKernel; + d->convolutionKernel = new qreal[rows * columns]; + memcpy(d->convolutionKernel, kernel, sizeof(qreal) * rows * columns); + d->kernelWidth = columns; + d->kernelHeight = rows; +} + +/*! + Gets the convolution kernel data. + + \internal +*/ +const qreal *QPixmapConvolutionFilter::convolutionKernel() const +{ + Q_D(const QPixmapConvolutionFilter); + return d->convolutionKernel; +} + +/*! + Gets the number of rows in the convolution kernel. + + \internal +*/ +int QPixmapConvolutionFilter::rows() const +{ + Q_D(const QPixmapConvolutionFilter); + return d->kernelHeight; +} + +/*! + Gets the number of columns in the convolution kernel. + + \internal +*/ +int QPixmapConvolutionFilter::columns() const +{ + Q_D(const QPixmapConvolutionFilter); + return d->kernelWidth; +} + + +/*! + \internal +*/ +QRectF QPixmapConvolutionFilter::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QPixmapConvolutionFilter); + return rect.adjusted(-d->kernelWidth / 2, -d->kernelHeight / 2, (d->kernelWidth - 1) / 2, (d->kernelHeight - 1) / 2); +} + +// Convolutes the image +static void convolute( + QImage *destImage, + const QPointF &pos, + const QImage &srcImage, + const QRectF &srcRect, + QPainter::CompositionMode mode, + qreal *kernel, + int kernelWidth, + int kernelHeight ) +{ + const QImage processImage = (srcImage.format() != QImage::Format_ARGB32_Premultiplied ) ? srcImage.convertToFormat(QImage::Format_ARGB32_Premultiplied) : srcImage; + // TODO: support also other formats directly without copying + + int *fixedKernel = new int[kernelWidth*kernelHeight]; + for(int i = 0; i < kernelWidth*kernelHeight; i++) + { + fixedKernel[i] = (int)(65536 * kernel[i]); + } + QRectF trect = srcRect.isNull() ? processImage.rect() : srcRect; + trect.moveTo(pos); + QRectF bounded = trect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2); + QRect rect = bounded.toAlignedRect(); + QRect targetRect = rect.intersected(destImage->rect()); + + QRectF srect = srcRect.isNull() ? processImage.rect() : srcRect; + QRectF sbounded = srect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2); + QPoint srcStartPoint = sbounded.toAlignedRect().topLeft()+(targetRect.topLeft()-rect.topLeft()); + + const uint *sourceStart = (uint*)processImage.scanLine(0); + uint *outputStart = (uint*)destImage->scanLine(0); + + int yk = srcStartPoint.y(); + for (int y = targetRect.top(); y <= targetRect.bottom(); y++) { + uint* output = outputStart + (destImage->bytesPerLine()/sizeof(uint))*y+targetRect.left(); + int xk = srcStartPoint.x(); + for(int x = targetRect.left(); x <= targetRect.right(); x++) { + int r = 0; + int g = 0; + int b = 0; + int a = 0; + + // some out of bounds pre-checking to avoid inner-loop ifs + int kernely = -kernelHeight/2; + int starty = 0; + int endy = kernelHeight; + if(yk+kernely+endy >= srcImage.height()) + endy = kernelHeight-((yk+kernely+endy)-srcImage.height())-1; + if(yk+kernely < 0) + starty = -(yk+kernely); + + int kernelx = -kernelWidth/2; + int startx = 0; + int endx = kernelWidth; + if(xk+kernelx+endx >= srcImage.width()) + endx = kernelWidth-((xk+kernelx+endx)-srcImage.width())-1; + if(xk+kernelx < 0) + startx = -(xk+kernelx); + + for (int ys = starty; ys < endy; ys ++) { + const uint *pix = sourceStart + (processImage.bytesPerLine()/sizeof(uint))*(yk+kernely+ys) + ((xk+kernelx+startx)); + const uint *endPix = pix+endx-startx; + int kernelPos = ys*kernelWidth+startx; + while (pix < endPix) { + int factor = fixedKernel[kernelPos++]; + a += (((*pix) & 0xff000000)>>24) * factor; + r += (((*pix) & 0x00ff0000)>>16) * factor; + g += (((*pix) & 0x0000ff00)>>8 ) * factor; + b += (((*pix) & 0x000000ff) ) * factor; + pix++; + } + } + + r = qBound((int)0, r >> 16, (int)255); + g = qBound((int)0, g >> 16, (int)255); + b = qBound((int)0, b >> 16, (int)255); + a = qBound((int)0, a >> 16, (int)255); + // composition mode checking could be moved outside of loop + if(mode == QPainter::CompositionMode_Source) { + uint color = (a<<24)+(r<<16)+(g<<8)+b; + *output++ = color; + } else { + uint current = *output; + uchar ca = (current&0xff000000)>>24; + uchar cr = (current&0x00ff0000)>>16; + uchar cg = (current&0x0000ff00)>>8; + uchar cb = (current&0x000000ff); + uint color = + (((ca*(255-a) >> 8)+a) << 24)+ + (((cr*(255-a) >> 8)+r) << 16)+ + (((cg*(255-a) >> 8)+g) << 8)+ + (((cb*(255-a) >> 8)+b)); + *output++ = color;; + } + xk++; + } + yk++; + } + delete[] fixedKernel; +} + +/*! + \internal +*/ +void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const +{ + Q_D(const QPixmapConvolutionFilter); + if (!painter->isActive()) + return; + + if(d->kernelWidth<=0 || d->kernelHeight <= 0) + return; + + if (src.isNull()) + return; + + QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ? + static_cast<QPaintEngineEx *>(painter->paintEngine())->pixmapFilter(type(), this) : 0; + QPixmapConvolutionFilter *convolutionFilter = static_cast<QPixmapConvolutionFilter*>(filter); + if (convolutionFilter) { + convolutionFilter->setConvolutionKernel(d->convolutionKernel, d->kernelWidth, d->kernelHeight); + convolutionFilter->d_func()->convoluteAlpha = d->convoluteAlpha; + convolutionFilter->draw(painter, p, src, srcRect); + return; + } + + // falling back to raster implementation + + QImage *target = 0; + if (painter->paintEngine()->paintDevice()->devType() == QInternal::Image) { + target = static_cast<QImage *>(painter->paintEngine()->paintDevice()); + + QTransform mat = painter->combinedTransform(); + + if (mat.type() > QTransform::TxTranslate) { + // Disabled because of transformation... + target = 0; + } else { + QRasterPaintEngine *pe = static_cast<QRasterPaintEngine *>(painter->paintEngine()); + if (pe->clipType() == QRasterPaintEngine::ComplexClip) + // disabled because of complex clipping... + target = 0; + else { + QRectF clip = pe->clipBoundingRect(); + QRectF rect = boundingRectFor(srcRect.isEmpty() ? src.rect() : srcRect); + QTransform x = painter->deviceTransform(); + if (!clip.contains(rect.translated(x.dx() + p.x(), x.dy() + p.y()))) { + target = 0; + } + + } + } + } + + if (target) { + QTransform x = painter->deviceTransform(); + QPointF offset(x.dx(), x.dy()); + + convolute(target, p+offset, src.toImage(), srcRect, QPainter::CompositionMode_SourceOver, d->convolutionKernel, d->kernelWidth, d->kernelHeight); + } else { + QRect srect = srcRect.isNull() ? src.rect() : srcRect.toRect(); + QRect rect = boundingRectFor(srect).toRect(); + QImage result = QImage(rect.size(), QImage::Format_ARGB32_Premultiplied); + QPoint offset = srect.topLeft() - rect.topLeft(); + convolute(&result, + offset, + src.toImage(), + srect, + QPainter::CompositionMode_Source, + d->convolutionKernel, + d->kernelWidth, + d->kernelHeight); + painter->drawImage(p - offset, result); + } +} + +/*! + \class QPixmapBlurFilter + \since 4.6 + \ingroup multimedia + + \brief The QPixmapBlurFilter class provides blur filtering + for pixmaps. + + QPixmapBlurFilter implements a blur pixmap filter, + which is applied when \l{QPixmapFilter::}{draw()} is called. + + The filter lets you specialize the radius of the blur as well + as hints as to whether to prefer performance or quality. + + By default, the blur effect is produced by applying an exponential + filter generated from the specified blurRadius(). Paint engines + may override this with a custom blur that is faster on the + underlying hardware. + + \sa {Pixmap Filters Example}, QPixmapConvolutionFilter, QPixmapDropShadowFilter + + \internal +*/ + +class QPixmapBlurFilterPrivate : public QPixmapFilterPrivate +{ +public: + QPixmapBlurFilterPrivate() : radius(5), hints(QGraphicsBlurEffect::PerformanceHint) {} + + qreal radius; + QGraphicsBlurEffect::BlurHints hints; +}; + + +/*! + Constructs a pixmap blur filter. + + \internal +*/ +QPixmapBlurFilter::QPixmapBlurFilter(QObject *parent) + : QPixmapFilter(*new QPixmapBlurFilterPrivate, BlurFilter, parent) +{ +} + +/*! + Destructor of pixmap blur filter. + + \internal +*/ +QPixmapBlurFilter::~QPixmapBlurFilter() +{ +} + +/*! + Sets the radius of the blur filter. Higher radius produces increased blurriness. + + \internal +*/ +void QPixmapBlurFilter::setRadius(qreal radius) +{ + Q_D(QPixmapBlurFilter); + d->radius = radius; +} + +/*! + Gets the radius of the blur filter. + + \internal +*/ +qreal QPixmapBlurFilter::radius() const +{ + Q_D(const QPixmapBlurFilter); + return d->radius; +} + +/*! + Setting the blur hints to PerformanceHint causes the implementation + to trade off visual quality to blur the image faster. Setting the + blur hints to QualityHint causes the implementation to improve + visual quality at the expense of speed. + + AnimationHint causes the implementation to optimize for animating + the blur radius, possibly by caching blurred versions of the source + pixmap. + + The implementation is free to ignore this value if it only has a single + blur algorithm. + + \internal +*/ +void QPixmapBlurFilter::setBlurHints(QGraphicsBlurEffect::BlurHints hints) +{ + Q_D(QPixmapBlurFilter); + d->hints = hints; +} + +/*! + Gets the blur hints of the blur filter. + + \internal +*/ +QGraphicsBlurEffect::BlurHints QPixmapBlurFilter::blurHints() const +{ + Q_D(const QPixmapBlurFilter); + return d->hints; +} + +const qreal radiusScale = qreal(2.5); + +/*! + \internal +*/ +QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QPixmapBlurFilter); + const qreal delta = radiusScale * d->radius + 1; + return rect.adjusted(-delta, -delta, delta, delta); +} + +template <int shift> +inline int qt_static_shift(int value) +{ + if (shift == 0) + return value; + else if (shift > 0) + return value << (uint(shift) & 0x1f); + else + return value >> (uint(-shift) & 0x1f); +} + +template<int aprec, int zprec> +inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, int alpha) +{ + QRgb *pixel = (QRgb *)bptr; + +#define Z_MASK (0xff << zprec) + const int A_zprec = qt_static_shift<zprec - 24>(*pixel) & Z_MASK; + const int R_zprec = qt_static_shift<zprec - 16>(*pixel) & Z_MASK; + const int G_zprec = qt_static_shift<zprec - 8>(*pixel) & Z_MASK; + const int B_zprec = qt_static_shift<zprec>(*pixel) & Z_MASK; +#undef Z_MASK + + const int zR_zprec = zR >> aprec; + const int zG_zprec = zG >> aprec; + const int zB_zprec = zB >> aprec; + const int zA_zprec = zA >> aprec; + + zR += alpha * (R_zprec - zR_zprec); + zG += alpha * (G_zprec - zG_zprec); + zB += alpha * (B_zprec - zB_zprec); + zA += alpha * (A_zprec - zA_zprec); + +#define ZA_MASK (0xff << (zprec + aprec)) + *pixel = + qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK) + | qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK) + | qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK) + | qt_static_shift<-zprec - aprec>(zB & ZA_MASK); +#undef ZA_MASK +} + +const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3); + +template<int aprec, int zprec> +inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, int alpha) +{ + const int A_zprec = int(*(bptr)) << zprec; + const int z_zprec = z >> aprec; + z += alpha * (A_zprec - z_zprec); + *(bptr) = z >> (zprec + aprec); +} + +template<int aprec, int zprec, bool alphaOnly> +inline void qt_blurrow(QImage & im, int line, int alpha) +{ + uchar *bptr = im.scanLine(line); + + int zR = 0, zG = 0, zB = 0, zA = 0; + + if (alphaOnly && im.format() != QImage::Format_Indexed8) + bptr += alphaIndex; + + const int stride = im.depth() >> 3; + const int im_width = im.width(); + for (int index = 0; index < im_width; ++index) { + if (alphaOnly) + qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha); + else + qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha); + bptr += stride; + } + + bptr -= stride; + + for (int index = im_width - 2; index >= 0; --index) { + bptr -= stride; + if (alphaOnly) + qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha); + else + qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha); + } +} + +/* +* expblur(QImage &img, int radius) +* +* Based on exponential blur algorithm by Jani Huhtanen +* +* In-place blur of image 'img' with kernel +* of approximate radius 'radius'. +* +* Blurs with two sided exponential impulse +* response. +* +* aprec = precision of alpha parameter +* in fixed-point format 0.aprec +* +* zprec = precision of state parameters +* zR,zG,zB and zA in fp format 8.zprec +*/ +template <int aprec, int zprec, bool alphaOnly> +void expblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0) +{ + // halve the radius if we're using two passes + if (improvedQuality) + radius *= qreal(0.5); + + Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied + || img.format() == QImage::Format_RGB32 + || img.format() == QImage::Format_Indexed8); + + // choose the alpha such that pixels at radius distance from a fully + // saturated pixel will have an alpha component of no greater than + // the cutOffIntensity + const qreal cutOffIntensity = 2; + int alpha = radius <= qreal(1e-5) + ? ((1 << aprec)-1) + : qRound((1<<aprec)*(1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / radius))); + + int img_height = img.height(); + for (int row = 0; row < img_height; ++row) { + for (int i = 0; i <= int(improvedQuality); ++i) + qt_blurrow<aprec, zprec, alphaOnly>(img, row, alpha); + } + + QImage temp(img.height(), img.width(), img.format()); + if (transposed >= 0) { + if (img.depth() == 8) { + qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()), + img.width(), img.height(), img.bytesPerLine(), + reinterpret_cast<quint8*>(temp.bits()), + temp.bytesPerLine()); + } else { + qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()), + img.width(), img.height(), img.bytesPerLine(), + reinterpret_cast<quint32*>(temp.bits()), + temp.bytesPerLine()); + } + } else { + if (img.depth() == 8) { + qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()), + img.width(), img.height(), img.bytesPerLine(), + reinterpret_cast<quint8*>(temp.bits()), + temp.bytesPerLine()); + } else { + qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()), + img.width(), img.height(), img.bytesPerLine(), + reinterpret_cast<quint32*>(temp.bits()), + temp.bytesPerLine()); + } + } + + img_height = temp.height(); + for (int row = 0; row < img_height; ++row) { + for (int i = 0; i <= int(improvedQuality); ++i) + qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha); + } + + if (transposed == 0) { + if (img.depth() == 8) { + qt_memrotate90(reinterpret_cast<const quint8*>(temp.bits()), + temp.width(), temp.height(), temp.bytesPerLine(), + reinterpret_cast<quint8*>(img.bits()), + img.bytesPerLine()); + } else { + qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()), + temp.width(), temp.height(), temp.bytesPerLine(), + reinterpret_cast<quint32*>(img.bits()), + img.bytesPerLine()); + } + } else { + img = temp; + } +} +#define AVG(a,b) ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) ) +#define AVG16(a,b) ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) ) + +Q_WIDGETS_EXPORT QImage qt_halfScaled(const QImage &source) +{ + if (source.width() < 2 || source.height() < 2) + return QImage(); + + QImage srcImage = source; + + if (source.format() == QImage::Format_Indexed8) { + // assumes grayscale + QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); + + const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits()); + int sx = srcImage.bytesPerLine(); + int sx2 = sx << 1; + + uchar *dst = reinterpret_cast<uchar*>(dest.bits()); + int dx = dest.bytesPerLine(); + int ww = dest.width(); + int hh = dest.height(); + + for (int y = hh; y; --y, dst += dx, src += sx2) { + const uchar *p1 = src; + const uchar *p2 = src + sx; + uchar *q = dst; + for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2) + *q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2; + } + + return dest; + } else if (source.format() == QImage::Format_ARGB8565_Premultiplied) { + QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); + + const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits()); + int sx = srcImage.bytesPerLine(); + int sx2 = sx << 1; + + uchar *dst = reinterpret_cast<uchar*>(dest.bits()); + int dx = dest.bytesPerLine(); + int ww = dest.width(); + int hh = dest.height(); + + for (int y = hh; y; --y, dst += dx, src += sx2) { + const uchar *p1 = src; + const uchar *p2 = src + sx; + uchar *q = dst; + for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) { + // alpha + q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3])); + // rgb + const quint16 p16_1 = (p1[2] << 8) | p1[1]; + const quint16 p16_2 = (p1[5] << 8) | p1[4]; + const quint16 p16_3 = (p2[2] << 8) | p2[1]; + const quint16 p16_4 = (p2[5] << 8) | p2[4]; + const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4)); + q[1] = result & 0xff; + q[2] = result >> 8; + } + } + + return dest; + } else if (source.format() != QImage::Format_ARGB32_Premultiplied + && source.format() != QImage::Format_RGB32) + { + srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied); + } + + QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); + + const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits()); + int sx = srcImage.bytesPerLine() >> 2; + int sx2 = sx << 1; + + quint32 *dst = reinterpret_cast<quint32*>(dest.bits()); + int dx = dest.bytesPerLine() >> 2; + int ww = dest.width(); + int hh = dest.height(); + + for (int y = hh; y; --y, dst += dx, src += sx2) { + const quint32 *p1 = src; + const quint32 *p2 = src + sx; + quint32 *q = dst; + for (int x = ww; x; --x, q++, p1 += 2, p2 += 2) + *q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1])); + } + + return dest; +} + +Q_WIDGETS_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0) +{ + if (blurImage.format() != QImage::Format_ARGB32_Premultiplied + && blurImage.format() != QImage::Format_RGB32) + { + blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied); + } + + qreal scale = 1; + if (radius >= 4 && blurImage.width() >= 2 && blurImage.height() >= 2) { + blurImage = qt_halfScaled(blurImage); + scale = 2; + radius *= qreal(0.5); + } + + if (alphaOnly) + expblur<12, 10, true>(blurImage, radius, quality, transposed); + else + expblur<12, 10, false>(blurImage, radius, quality, transposed); + + if (p) { + p->scale(scale, scale); + p->setRenderHint(QPainter::SmoothPixmapTransform); + p->drawImage(QRect(0, 0, blurImage.width(), blurImage.height()), blurImage); + } +} + +Q_WIDGETS_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0) +{ + if (blurImage.format() == QImage::Format_Indexed8) + expblur<12, 10, true>(blurImage, radius, quality, transposed); + else + expblur<12, 10, false>(blurImage, radius, quality, transposed); +} + +Q_WIDGETS_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); + +/*! + \internal +*/ +void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &rect) const +{ + Q_D(const QPixmapBlurFilter); + if (!painter->isActive()) + return; + + if (src.isNull()) + return; + + QRectF srcRect = rect; + if (srcRect.isNull()) + srcRect = src.rect(); + + if (d->radius <= 1) { + painter->drawPixmap(srcRect.translated(p), src, srcRect); + return; + } + + qreal scaledRadius = radiusScale * d->radius; + qreal scale; + if (qt_scaleForTransform(painter->transform(), &scale)) + scaledRadius /= scale; + + QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ? + static_cast<QPaintEngineEx *>(painter->paintEngine())->pixmapFilter(type(), this) : 0; + QPixmapBlurFilter *blurFilter = static_cast<QPixmapBlurFilter*>(filter); + if (blurFilter) { + blurFilter->setRadius(scaledRadius); + blurFilter->setBlurHints(d->hints); + blurFilter->draw(painter, p, src, srcRect); + return; + } + + QImage srcImage; + QImage destImage; + + if (srcRect == src.rect()) { + srcImage = src.toImage(); + } else { + QRect rect = srcRect.toAlignedRect().intersected(src.rect()); + srcImage = src.copy(rect).toImage(); + } + + QTransform transform = painter->worldTransform(); + painter->translate(p); + qt_blurImage(painter, srcImage, scaledRadius, (d->hints & QGraphicsBlurEffect::QualityHint), false); + painter->setWorldTransform(transform); +} + +// grayscales the image to dest (could be same). If rect isn't defined +// destination image size is used to determine the dimension of grayscaling +// process. +static void grayscale(const QImage &image, QImage &dest, const QRect& rect = QRect()) +{ + QRect destRect = rect; + QRect srcRect = rect; + if (rect.isNull()) { + srcRect = dest.rect(); + destRect = dest.rect(); + } + if (&image != &dest) { + destRect.moveTo(QPoint(0, 0)); + } + + unsigned int *data = (unsigned int *)image.bits(); + unsigned int *outData = (unsigned int *)dest.bits(); + + if (dest.size() == image.size() && image.rect() == srcRect) { + // a bit faster loop for grayscaling everything + int pixels = dest.width() * dest.height(); + for (int i = 0; i < pixels; ++i) { + int val = qGray(data[i]); + outData[i] = qRgba(val, val, val, qAlpha(data[i])); + } + } else { + int yd = destRect.top(); + for (int y = srcRect.top(); y <= srcRect.bottom() && y < image.height(); y++) { + data = (unsigned int*)image.scanLine(y); + outData = (unsigned int*)dest.scanLine(yd++); + int xd = destRect.left(); + for (int x = srcRect.left(); x <= srcRect.right() && x < image.width(); x++) { + int val = qGray(data[x]); + outData[xd++] = qRgba(val, val, val, qAlpha(data[x])); + } + } + } +} + +/*! + \class QPixmapColorizeFilter + \since 4.5 + \ingroup painting + + \brief The QPixmapColorizeFilter class provides colorizing + filtering for pixmaps. + + A colorize filter gives the pixmap a tint of its color(). The + filter first grayscales the pixmap and then converts those to + colorized values using QPainter::CompositionMode_Screen with the + chosen color. The alpha-channel is not changed. + + Example: + \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 0 + + \sa QPainter::CompositionMode + + \internal +*/ +class QPixmapColorizeFilterPrivate : public QPixmapFilterPrivate +{ + Q_DECLARE_PUBLIC(QPixmapColorizeFilter) +public: + QColor color; + qreal strength; + quint32 opaque : 1; + quint32 alphaBlend : 1; + quint32 padding : 30; +}; + +/*! + Constructs an pixmap colorize filter. + + Default color value for colorizing is QColor(0, 0, 192). + + \internal +*/ +QPixmapColorizeFilter::QPixmapColorizeFilter(QObject *parent) + : QPixmapFilter(*new QPixmapColorizeFilterPrivate, ColorizeFilter, parent) +{ + Q_D(QPixmapColorizeFilter); + d->color = QColor(0, 0, 192); + d->strength = qreal(1); + d->opaque = true; + d->alphaBlend = false; +} + +/*! + Gets the color of the colorize filter. + + \internal +*/ +QColor QPixmapColorizeFilter::color() const +{ + Q_D(const QPixmapColorizeFilter); + return d->color; +} + +/*! + Sets the color of the colorize filter to the \a color specified. + + \internal +*/ +void QPixmapColorizeFilter::setColor(const QColor &color) +{ + Q_D(QPixmapColorizeFilter); + d->color = color; +} + +/*! + Gets the strength of the colorize filter, 1.0 means full colorized while + 0.0 equals to no filtering at all. + + \internal +*/ +qreal QPixmapColorizeFilter::strength() const +{ + Q_D(const QPixmapColorizeFilter); + return d->strength; +} + +/*! + Sets the strength of the colorize filter to \a strength. + + \internal +*/ +void QPixmapColorizeFilter::setStrength(qreal strength) +{ + Q_D(QPixmapColorizeFilter); + d->strength = qBound(qreal(0), strength, qreal(1)); + d->opaque = !qFuzzyIsNull(d->strength); + d->alphaBlend = !qFuzzyIsNull(d->strength - 1); +} + +/*! + \internal +*/ +void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const +{ + Q_D(const QPixmapColorizeFilter); + + if (src.isNull()) + return; + + QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ? + static_cast<QPaintEngineEx *>(painter->paintEngine())->pixmapFilter(type(), this) : 0; + QPixmapColorizeFilter *colorizeFilter = static_cast<QPixmapColorizeFilter*>(filter); + if (colorizeFilter) { + colorizeFilter->setColor(d->color); + colorizeFilter->setStrength(d->strength); + colorizeFilter->draw(painter, dest, src, srcRect); + return; + } + + // falling back to raster implementation + + if (!d->opaque) { + painter->drawPixmap(dest, src, srcRect); + return; + } + + QImage srcImage; + QImage destImage; + + if (srcRect.isNull()) { + srcImage = src.toImage(); + srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); + destImage = QImage(srcImage.size(), srcImage.format()); + } else { + QRect rect = srcRect.toAlignedRect().intersected(src.rect()); + + srcImage = src.copy(rect).toImage(); + srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); + destImage = QImage(rect.size(), srcImage.format()); + } + + // do colorizing + QPainter destPainter(&destImage); + grayscale(srcImage, destImage, srcImage.rect()); + destPainter.setCompositionMode(QPainter::CompositionMode_Screen); + destPainter.fillRect(srcImage.rect(), d->color); + destPainter.end(); + + if (d->alphaBlend) { + // alpha blending srcImage and destImage + QImage buffer = srcImage; + QPainter bufPainter(&buffer); + bufPainter.setOpacity(d->strength); + bufPainter.drawImage(0, 0, destImage); + bufPainter.end(); + destImage = buffer; + } + + if (srcImage.hasAlphaChannel()) + destImage.setAlphaChannel(srcImage.alphaChannel()); + + painter->drawImage(dest, destImage); +} + +class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate +{ +public: + QPixmapDropShadowFilterPrivate() + : offset(8, 8), color(63, 63, 63, 180), radius(1) {} + + QPointF offset; + QColor color; + qreal radius; +}; + +/*! + \class QPixmapDropShadowFilter + \since 4.5 + \ingroup painting + + \brief The QPixmapDropShadowFilter class is a convenience class + for drawing pixmaps with drop shadows. + + The drop shadow is produced by taking a copy of the source pixmap + and applying a color to the copy using a + QPainter::CompositionMode_DestinationIn operation. This produces a + homogeneously-colored pixmap which is then drawn using a + QPixmapConvolutionFilter at an offset. The original pixmap is + drawn on top. + + The QPixmapDropShadowFilter class provides some customization + options to specify how the drop shadow should appear. The color of + the drop shadow can be modified using the setColor() function, the + drop shadow offset can be modified using the setOffset() function, + and the blur radius of the drop shadow can be changed through the + setBlurRadius() function. + + By default, the drop shadow is a dark gray shadow, blurred with a + radius of 1 at an offset of 8 pixels towards the lower right. + + Example: + \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 2 + + \sa QPixmapColorizeFilter, QPixmapConvolutionFilter + + \internal + */ + +/*! + Constructs drop shadow filter. + + \internal +*/ +QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent) + : QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent) +{ +} + +/*! + Destroys drop shadow filter. + + \internal +*/ +QPixmapDropShadowFilter::~QPixmapDropShadowFilter() +{ +} + +/*! + Returns the radius in pixels of the blur on the drop shadow. + + A smaller radius results in a sharper shadow. + + \sa color(), offset() + + \internal +*/ +qreal QPixmapDropShadowFilter::blurRadius() const +{ + Q_D(const QPixmapDropShadowFilter); + return d->radius; +} + +/*! + Sets the radius in pixels of the blur on the drop shadow to the \a radius specified. + + Using a smaller radius results in a sharper shadow. + + \sa setColor(), setOffset() + + \internal +*/ +void QPixmapDropShadowFilter::setBlurRadius(qreal radius) +{ + Q_D(QPixmapDropShadowFilter); + d->radius = radius; +} + +/*! + Returns the color of the drop shadow. + + \sa blurRadius(), offset() + + \internal +*/ +QColor QPixmapDropShadowFilter::color() const +{ + Q_D(const QPixmapDropShadowFilter); + return d->color; +} + +/*! + Sets the color of the drop shadow to the \a color specified. + + \sa setBlurRadius(), setOffset() + + \internal +*/ +void QPixmapDropShadowFilter::setColor(const QColor &color) +{ + Q_D(QPixmapDropShadowFilter); + d->color = color; +} + +/*! + Returns the shadow offset in pixels. + + \sa blurRadius(), color() + + \internal +*/ +QPointF QPixmapDropShadowFilter::offset() const +{ + Q_D(const QPixmapDropShadowFilter); + return d->offset; +} + +/*! + Sets the shadow offset in pixels to the \a offset specified. + + \sa setBlurRadius(), setColor() + + \internal +*/ +void QPixmapDropShadowFilter::setOffset(const QPointF &offset) +{ + Q_D(QPixmapDropShadowFilter); + d->offset = offset; +} + +/*! + \fn void QPixmapDropShadowFilter::setOffset(qreal dx, qreal dy) + \overload + + Sets the shadow offset in pixels to be the displacement specified by the + horizontal \a dx and vertical \a dy coordinates. + + \sa setBlurRadius(), setColor() + + \internal +*/ + +/*! + \internal + */ +QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QPixmapDropShadowFilter); + return rect.united(rect.translated(d->offset).adjusted(-d->radius, -d->radius, d->radius, d->radius)); +} + +/*! + \internal + */ +void QPixmapDropShadowFilter::draw(QPainter *p, + const QPointF &pos, + const QPixmap &px, + const QRectF &src) const +{ + Q_D(const QPixmapDropShadowFilter); + + if (px.isNull()) + return; + + QPixmapFilter *filter = p->paintEngine() && p->paintEngine()->isExtended() ? + static_cast<QPaintEngineEx *>(p->paintEngine())->pixmapFilter(type(), this) : 0; + QPixmapDropShadowFilter *dropShadowFilter = static_cast<QPixmapDropShadowFilter*>(filter); + if (dropShadowFilter) { + dropShadowFilter->setColor(d->color); + dropShadowFilter->setBlurRadius(d->radius); + dropShadowFilter->setOffset(d->offset); + dropShadowFilter->draw(p, pos, px, src); + return; + } + + QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied); + tmp.fill(0); + QPainter tmpPainter(&tmp); + tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); + tmpPainter.drawPixmap(d->offset, px); + tmpPainter.end(); + + // blur the alpha channel + QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied); + blurred.fill(0); + QPainter blurPainter(&blurred); + qt_blurImage(&blurPainter, tmp, d->radius, false, true); + blurPainter.end(); + + tmp = blurred; + + // blacken the image... + tmpPainter.begin(&tmp); + tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + tmpPainter.fillRect(tmp.rect(), d->color); + tmpPainter.end(); + + // draw the blurred drop shadow... + p->drawImage(pos, tmp); + + // Draw the actual pixmap... + p->drawPixmap(pos, px, src); +} + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSEFFECT diff --git a/src/widgets/effects/qpixmapfilter_p.h b/src/widgets/effects/qpixmapfilter_p.h new file mode 100644 index 0000000000..b0edd8d4b0 --- /dev/null +++ b/src/widgets/effects/qpixmapfilter_p.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPIXMAPFILTER_H +#define QPIXMAPFILTER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qnamespace.h> +#include <QtGui/qpixmap.h> +#include <QtWidgets/qgraphicseffect.h> + +#ifndef QT_NO_GRAPHICSEFFECT +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPainter; +class QPlatformPixmap; + +class QPixmapFilterPrivate; + +class Q_WIDGETS_EXPORT QPixmapFilter : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPixmapFilter) +public: + virtual ~QPixmapFilter() = 0; + + enum FilterType { + ConvolutionFilter, + ColorizeFilter, + DropShadowFilter, + BlurFilter, + + UserFilter = 1024 + }; + + FilterType type() const; + + virtual QRectF boundingRectFor(const QRectF &rect) const; + + virtual void draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &srcRect = QRectF()) const = 0; + +protected: + QPixmapFilter(QPixmapFilterPrivate &d, FilterType type, QObject *parent); + QPixmapFilter(FilterType type, QObject *parent); +}; + +class QPixmapConvolutionFilterPrivate; + +class Q_WIDGETS_EXPORT QPixmapConvolutionFilter : public QPixmapFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPixmapConvolutionFilter) + +public: + QPixmapConvolutionFilter(QObject *parent = 0); + ~QPixmapConvolutionFilter(); + + void setConvolutionKernel(const qreal *matrix, int rows, int columns); + + QRectF boundingRectFor(const QRectF &rect) const; + void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const; + +private: + friend class QGLPixmapConvolutionFilter; + friend class QVGPixmapConvolutionFilter; + const qreal *convolutionKernel() const; + int rows() const; + int columns() const; +}; + +class QPixmapBlurFilterPrivate; + +class Q_WIDGETS_EXPORT QPixmapBlurFilter : public QPixmapFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPixmapBlurFilter) + +public: + QPixmapBlurFilter(QObject *parent = 0); + ~QPixmapBlurFilter(); + + void setRadius(qreal radius); + void setBlurHints(QGraphicsBlurEffect::BlurHints hints); + + qreal radius() const; + QGraphicsBlurEffect::BlurHints blurHints() const; + + QRectF boundingRectFor(const QRectF &rect) const; + void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const; + +private: + friend class QGLPixmapBlurFilter; +}; + +class QPixmapColorizeFilterPrivate; + +class Q_WIDGETS_EXPORT QPixmapColorizeFilter : public QPixmapFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPixmapColorizeFilter) + +public: + QPixmapColorizeFilter(QObject *parent = 0); + + void setColor(const QColor& color); + QColor color() const; + + void setStrength(qreal strength); + qreal strength() const; + + void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const; +}; + +class QPixmapDropShadowFilterPrivate; + +class Q_WIDGETS_EXPORT QPixmapDropShadowFilter : public QPixmapFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPixmapDropShadowFilter) + +public: + QPixmapDropShadowFilter(QObject *parent = 0); + ~QPixmapDropShadowFilter(); + + QRectF boundingRectFor(const QRectF &rect) const; + void draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src = QRectF()) const; + + qreal blurRadius() const; + void setBlurRadius(qreal radius); + + QColor color() const; + void setColor(const QColor &color); + + QPointF offset() const; + void setOffset(const QPointF &offset); + inline void setOffset(qreal dx, qreal dy) { setOffset(QPointF(dx, dy)); } +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QT_NO_GRAPHICSEFFECT +#endif // QPIXMAPFILTER_H |