diff options
Diffstat (limited to 'src/gui/image/qpixmapfilter.cpp')
-rw-r--r-- | src/gui/image/qpixmapfilter.cpp | 286 |
1 files changed, 243 insertions, 43 deletions
diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp index 3358091..b2d7f0c 100644 --- a/src/gui/image/qpixmapfilter.cpp +++ b/src/gui/image/qpixmapfilter.cpp @@ -65,7 +65,7 @@ public: /*! \class QPixmapFilter \since 4.5 - \ingroup multimedia + \ingroup painting \brief The QPixmapFilter class provides the basic functionality for pixmap filter classes. Pixmap filter can be for example colorize or blur. @@ -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. @@ -172,7 +175,7 @@ QRectF QPixmapFilter::boundingRectFor(const QRectF &rect) const /*! \class QPixmapConvolutionFilter \since 4.5 - \ingroup multimedia + \ingroup painting \brief The QPixmapConvolutionFilter class provides convolution filtering for pixmaps. @@ -293,8 +296,6 @@ int QPixmapConvolutionFilter::columns() const /*! - \reimp - \internal */ QRectF QPixmapConvolutionFilter::boundingRectFor(const QRectF &rect) const @@ -401,11 +402,10 @@ static void convolute( } yk++; } + delete[] fixedKernel; } /*! - \reimp - \internal */ void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const @@ -478,6 +478,223 @@ void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const Q } } +/*! + \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 the 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), quality(Qt::FastTransformation) {} + + int radius; + Qt::TransformationMode quality; +}; + + +/*! + 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(int radius) +{ + Q_D(QPixmapBlurFilter); + d->radius = radius; +} + +/*! + Gets the radius of the blur filter. + + \internal +*/ +int QPixmapBlurFilter::radius() const +{ + Q_D(const QPixmapBlurFilter); + return d->radius; +} + +/*! + Setting the quality to FastTransformation causes the implementation + to trade off visual quality to blur the image faster. Setting the + quality to SmoothTransformation 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. + + \internal +*/ +void QPixmapBlurFilter::setQuality(Qt::TransformationMode quality) +{ + Q_D(QPixmapBlurFilter); + d->quality = quality; +} + +/*! + Gets the quality of the blur filter. + + \internal +*/ +Qt::TransformationMode QPixmapBlurFilter::quality() const +{ + Q_D(const QPixmapBlurFilter); + return d->quality; +} + +/*! + \internal +*/ +QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QPixmapBlurFilter); + const qreal delta = d->radius * 2; + return rect.adjusted(-delta, -delta, delta, delta); +} + +// 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 &p, const QPixmap &src, const QRectF &srcRect) const +{ + Q_D(const QPixmapBlurFilter); + if (!painter->isActive()) + return; + + if (d->radius == 0) { + painter->drawPixmap(srcRect.translated(p), src, srcRect); + return; + } + + QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ? + static_cast<QPaintEngineEx *>(painter->paintEngine())->createPixmapFilter(type()) : 0; + QPixmapBlurFilter *blurFilter = static_cast<QPixmapBlurFilter*>(filter); + if (blurFilter) { + blurFilter->setRadius(d->radius); + blurFilter->setQuality(d->quality); + blurFilter->draw(painter, p, src, srcRect); + delete blurFilter; + return; + } + + QImage srcImage; + QImage destImage; + + if (srcRect.isNull()) { + srcImage = src.toImage(); + destImage = blurred(srcImage, srcImage.rect(), d->radius); + } else { + QRect rect = srcRect.toAlignedRect().intersected(src.rect()); + + srcImage = src.copy(rect).toImage(); + destImage = blurred(srcImage, srcImage.rect(), d->radius); + } + + painter->drawImage(p, destImage); +} + // 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. @@ -520,7 +737,7 @@ static void grayscale(const QImage &image, QImage &dest, const QRect& rect = QRe /*! \class QPixmapColorizeFilter \since 4.5 - \ingroup multimedia + \ingroup painting \brief The QPixmapColorizeFilter class provides colorizing filtering for pixmaps. @@ -580,8 +797,6 @@ void QPixmapColorizeFilter::setColor(const QColor &color) } /*! - \reimp - \internal */ void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const @@ -631,22 +846,17 @@ class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate { public: QPixmapDropShadowFilterPrivate() - : offset(8, 8), - radius(1), - color(63, 63, 63, 255) { - } + : offset(8, 8), color(63, 63, 63, 180), blurFilter(new QPixmapBlurFilter) {} QPointF offset; - qreal radius; QColor color; - - QPixmapConvolutionFilter *convolution; + QPixmapBlurFilter *blurFilter; }; /*! \class QPixmapDropShadowFilter \since 4.5 - \ingroup multimedia + \ingroup painting \brief The QPixmapDropShadowFilter class is a convenience class for drawing pixmaps with drop shadows. @@ -685,9 +895,7 @@ QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent) : QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent) { Q_D(QPixmapDropShadowFilter); - d->convolution = new QPixmapConvolutionFilter; - - setBlurRadius(1); + d->blurFilter->setRadius(1); } /*! @@ -698,7 +906,7 @@ QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent) QPixmapDropShadowFilter::~QPixmapDropShadowFilter() { Q_D(QPixmapDropShadowFilter); - delete d->convolution; + delete d->blurFilter; } /*! @@ -710,10 +918,10 @@ QPixmapDropShadowFilter::~QPixmapDropShadowFilter() \internal */ -qreal QPixmapDropShadowFilter::blurRadius() const +int QPixmapDropShadowFilter::blurRadius() const { Q_D(const QPixmapDropShadowFilter); - return d->radius; + return d->blurFilter->radius(); } /*! @@ -725,18 +933,10 @@ qreal QPixmapDropShadowFilter::blurRadius() const \internal */ -void QPixmapDropShadowFilter::setBlurRadius(qreal radius) +void QPixmapDropShadowFilter::setBlurRadius(int radius) { Q_D(QPixmapDropShadowFilter); - - d->radius = radius; - - int dim = 2 * qRound(radius) + 1; - QVarLengthArray<qreal> arr(dim * dim); - qreal f = 1 / qreal(dim * dim); - for (int i = 0; i < dim * dim; ++i) - arr[i] = f; - d->convolution->setConvolutionKernel(arr.data(), dim, dim); + d->blurFilter->setRadius(radius); } /*! @@ -804,25 +1004,22 @@ void QPixmapDropShadowFilter::setOffset(const QPointF &offset) */ /*! - \reimp - \internal */ QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const { Q_D(const QPixmapDropShadowFilter); - qreal x1 = qMin(rect.left(), rect.left() + d->offset.x() - d->radius); - qreal y1 = qMin(rect.top(), rect.top() + d->offset.y() - d->radius); - qreal x2 = qMax(rect.right(), rect.right() + d->offset.x() + d->radius); - qreal y2 = qMax(rect.bottom(), rect.bottom() + d->offset.y() + d->radius); + const qreal delta = qreal(d->blurFilter->radius() * 2); + qreal x1 = qMin(rect.left(), rect.left() + d->offset.x() - delta); + qreal y1 = qMin(rect.top(), rect.top() + d->offset.y() - delta); + qreal x2 = qMax(rect.right(), rect.right() + d->offset.x() + delta); + qreal y2 = qMax(rect.bottom(), rect.bottom() + d->offset.y() + delta); return QRectF(x1, y1, x2 - x1, y2 - y1); } /*! - \reimp - \internal */ void QPixmapDropShadowFilter::draw(QPainter *p, @@ -832,7 +1029,7 @@ void QPixmapDropShadowFilter::draw(QPainter *p, { Q_D(const QPixmapDropShadowFilter); - QPixmap tmp = src.isNull() ? px : px.copy(src.toRect()); + QImage tmp = src.isNull() ? px.toImage() : px.copy(src.toRect()).toImage(); QPainter tmpPainter(&tmp); // blacken the image... @@ -840,10 +1037,13 @@ void QPixmapDropShadowFilter::draw(QPainter *p, tmpPainter.fillRect(0, 0, tmp.width(), tmp.height(), d->color); tmpPainter.end(); + const QPixmap pixTmp = QPixmap::fromImage(tmp); + // draw the blurred drop shadow... - d->convolution->draw(p, pos + d->offset, tmp); + d->blurFilter->draw(p, pos + d->offset, pixTmp); // Draw the actual pixmap... p->drawPixmap(pos, px, src); } + QT_END_NAMESPACE |