From 787f2252077434581101df64d0f0d576c26b7ce8 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Thu, 23 Jul 2009 13:53:06 +1000 Subject: Add QPixmapBlurFilter for non-convolution blur effects. Reviewed-by: trustme --- src/gui/graphicsview/qgraphicseffect.cpp | 28 ++-- src/gui/image/qpixmapfilter.cpp | 235 +++++++++++++++++++++++++++++++ src/gui/image/qpixmapfilter_p.h | 28 ++++ 3 files changed, 276 insertions(+), 15 deletions(-) diff --git a/src/gui/graphicsview/qgraphicseffect.cpp b/src/gui/graphicsview/qgraphicseffect.cpp index a1519c4..6b1f12a 100644 --- a/src/gui/graphicsview/qgraphicseffect.cpp +++ b/src/gui/graphicsview/qgraphicseffect.cpp @@ -346,9 +346,15 @@ class QGraphicsBlurEffectPrivate : public QGraphicsEffectPrivate Q_DECLARE_PUBLIC(QGraphicsBlurEffect) public: QGraphicsBlurEffectPrivate() - : blurRadius(4) { } + { + filter = new QPixmapBlurFilter; + } + ~QGraphicsBlurEffectPrivate() + { + delete filter; + } - int blurRadius; + QPixmapBlurFilter *filter; }; QGraphicsBlurEffect::QGraphicsBlurEffect(QObject *parent) @@ -428,22 +434,19 @@ static QImage blurred(const QImage& image, const QRect& rect, int radius) int QGraphicsBlurEffect::blurRadius() const { Q_D(const QGraphicsBlurEffect); - return d->blurRadius; + return int(d->filter->blurRadius()); } void QGraphicsBlurEffect::setBlurRadius(int radius) { Q_D(QGraphicsBlurEffect); - d->blurRadius = radius; + d->filter->setBlurRadius(radius); } QRectF QGraphicsBlurEffect::boundingRectFor(const QGraphicsItem *item) { Q_D(const QGraphicsBlurEffect); - qreal delta = d->blurRadius * 3; - QRectF blurRect = item->boundingRect(); - blurRect.adjust(-delta, -delta, delta, delta); - return blurRect; + return d->filter->boundingRectFor(item->boundingRect()); } void QGraphicsBlurEffect::drawItem(QGraphicsItem *item, QPainter *painter, @@ -461,15 +464,10 @@ void QGraphicsBlurEffect::drawItem(QGraphicsItem *item, QPainter *painter, if (!pixmap) return; - // blur routine - int radius = d->blurRadius; - QImage img = pixmap->toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); - img = blurred(img, img.rect(), radius); - - // Draw using an untransformed painter. + // Draw the pixmap with the filter using an untransformed painter. QTransform restoreTransform = painter->worldTransform(); painter->setWorldTransform(QTransform()); - painter->drawImage(deviceRect.topLeft() - QPointF(radius * 3, radius * 3), img); + d->filter->draw(painter, deviceRect.topLeft(), *pixmap, pixmap->rect()); painter->setWorldTransform(restoreTransform); } diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp index 184bd65..2b275a3 100644 --- a/src/gui/image/qpixmapfilter.cpp +++ b/src/gui/image/qpixmapfilter.cpp @@ -93,6 +93,9 @@ public: \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. @@ -837,4 +840,236 @@ void QPixmapDropShadowFilter::draw(QPainter *p, // Draw the actual pixmap... p->drawPixmap(pos, px, src); } + +/*! + \class QPixmapBlurFilter + \since 4.6 + \ingroup multimedia + + \brief The QPixmapBlurFilter class is a convenience class + for drawing pixmaps with blur effects. + + 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 QPixmapConvolutionFilter + + \internal + */ + +class QPixmapBlurFilterPrivate : public QPixmapFilterPrivate +{ +public: + QPixmapBlurFilterPrivate() + : quality(QPixmapBlurFilter::High), radius(1) {} + + QPixmapBlurFilter::BlurQuality quality; + qreal radius; +}; + +/*! + Constructs blur filter and attaches it to \a parent. + + \internal +*/ +QPixmapBlurFilter::QPixmapBlurFilter(QObject *parent) + : QPixmapFilter(*new QPixmapBlurFilterPrivate, BlurFilter, parent) +{ + Q_D(QPixmapBlurFilter); + setBlurRadius(4); +} + +/*! + Destroys blur filter. + + \internal +*/ +QPixmapBlurFilter::~QPixmapBlurFilter() +{ +} + +/*! + \enum QPixmapFilter::BlurQuality + \since 4.6 + \ingroup multimedia + This enum describes the quality of blur to apply to pixmaps. + + \value Fast Blur faster, potentially losing some quality. + \value High Produce the best high-quality blur possible, even if slower. + + \internal +*/ + +/*! + Returns the quality of the blur. The default value is High. + + \sa blurRadius() + \internal +*/ +QPixmapBlurFilter::BlurQuality QPixmapBlurFilter::blurQuality() const +{ + Q_D(const QPixmapBlurFilter); + return d->quality; +} + +/*! + Sets the quality of the blur to the \a blurQuality specified. + + Setting the quality to Faster causes the implementation to trade + off visual quality to blur the image faster. Setting the quality + to High causes the implementation to improve visual quality + at the expense of speed. The implementation is free to ignore + this value if it only has a single blur algorithm. + + \sa setBlurRadius() + \internal +*/ +void QPixmapBlurFilter::setBlurQuality(BlurQuality blurQuality) +{ + Q_D(QPixmapBlurFilter); + d->quality = blurQuality; +} + +/*! + Returns the radius in pixels of the blur. The default value is 4. + + A smaller radius results in a sharper image. + + \sa blurQuality() + \internal +*/ +qreal QPixmapBlurFilter::blurRadius() const +{ + Q_D(const QPixmapBlurFilter); + return d->radius; +} + +/*! + Sets the radius in pixels of the blur to the \a radius specified. + + Using a smaller radius results in a sharper image. + + \sa setBlurQuality() + \internal +*/ +void QPixmapBlurFilter::setBlurRadius(qreal blurRadius) +{ + Q_D(QPixmapBlurFilter); + d->radius = blurRadius; +} + +/*! + \internal + */ +QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QPixmapBlurFilter); + qreal delta = d->radius * 3; + QRectF blurRect(rect); + blurRect.adjust(-delta, -delta, delta, delta); + return blurRect; +} + +// Blur the image according to the blur radius +// Based on exponential blur algorithm by Jani Huhtanen +// (maximum radius is set to 16) +static QImage blurred(const QImage& image, const QRect& rect, int radius) +{ + int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 }; + int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1]; + + QImage result = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + int r1 = rect.top(); + int r2 = rect.bottom(); + int c1 = rect.left(); + int c2 = rect.right(); + + int bpl = result.bytesPerLine(); + int rgba[4]; + unsigned char* p; + + for (int col = c1; col <= c2; col++) { + p = result.scanLine(r1) + col * 4; + for (int i = 0; i < 4; i++) + rgba[i] = p[i] << 4; + + p += bpl; + for (int j = r1; j < r2; j++, p += bpl) + for (int i = 0; i < 4; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + for (int row = r1; row <= r2; row++) { + p = result.scanLine(row) + c1 * 4; + for (int i = 0; i < 4; i++) + rgba[i] = p[i] << 4; + + p += 4; + for (int j = c1; j < c2; j++, p += 4) + for (int i = 0; i < 4; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + for (int col = c1; col <= c2; col++) { + p = result.scanLine(r2) + col * 4; + for (int i = 0; i < 4; i++) + rgba[i] = p[i] << 4; + + p -= bpl; + for (int j = r1; j < r2; j++, p -= bpl) + for (int i = 0; i < 4; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + for (int row = r1; row <= r2; row++) { + p = result.scanLine(row) + c2 * 4; + for (int i = 0; i < 4; i++) + rgba[i] = p[i] << 4; + + p -= 4; + for (int j = c1; j < c2; j++, p -= 4) + for (int i = 0; i < 4; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + return result; +} + +/*! + \internal + */ +void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const +{ + Q_D(const QPixmapBlurFilter); + + QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ? + static_cast(painter->paintEngine())->createPixmapFilter(type()) : 0; + QPixmapBlurFilter *blurFilter = static_cast(filter); + if (blurFilter) { + blurFilter->setBlurQuality(d->quality); + blurFilter->setBlurRadius(d->radius); + blurFilter->draw(painter, dest, src, srcRect); + delete blurFilter; + return; + } + + QImage srcImage; + QImage destImage; + + if (srcRect.isNull()) { + srcImage = src.toImage(); + destImage = blurred(srcImage, srcImage.rect(), int(d->radius + 0.5)); + } else { + QRect rect = srcRect.toAlignedRect().intersected(src.rect()); + + srcImage = src.copy(rect).toImage(); + destImage = blurred(srcImage, srcImage.rect(), int(d->radius + 0.5)); + } + + qreal delta = d->radius * 3; + painter->drawImage(dest - QPointF(delta, delta), destImage); +} + QT_END_NAMESPACE diff --git a/src/gui/image/qpixmapfilter_p.h b/src/gui/image/qpixmapfilter_p.h index 51292b3..ca27cbf 100644 --- a/src/gui/image/qpixmapfilter_p.h +++ b/src/gui/image/qpixmapfilter_p.h @@ -78,6 +78,7 @@ public: ConvolutionFilter, ColorizeFilter, DropShadowFilter, + BlurFilter, UserFilter = 1024 }; @@ -158,6 +159,33 @@ public: inline void setOffset(qreal dx, qreal dy) { setOffset(QPointF(dx, dy)); } }; +class QPixmapBlurFilterPrivate; + +class Q_GUI_EXPORT QPixmapBlurFilter : public QPixmapFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPixmapBlurFilter) + +public: + QPixmapBlurFilter(QObject *parent = 0); + ~QPixmapBlurFilter(); + + enum BlurQuality + { + Fast, + High + }; + + BlurQuality blurQuality() const; + void setBlurQuality(BlurQuality blurQuality); + + qreal blurRadius() const; + void setBlurRadius(qreal blurRadius); + + QRectF boundingRectFor(const QRectF &rect) const; + void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const; +}; + QT_END_NAMESPACE QT_END_HEADER -- cgit v0.12