diff options
Diffstat (limited to 'src')
30 files changed, 1156 insertions, 808 deletions
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 6e6da19..72d6786 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -2648,6 +2648,7 @@ const char* QMetaClassInfo::value() const */ int QMetaObjectPrivate::originalClone(const QMetaObject *mobj, int local_method_index) { + Q_ASSERT(local_method_index < get(mobj)->methodCount); int handle = get(mobj)->methodData + 5 * local_method_index; while (mobj->d.data[handle + 4] & MethodCloned) { Q_ASSERT(local_method_index > 0); diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 85915c2..4370ee0 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2850,6 +2850,27 @@ void QObject::disconnectNotify(const char *) { } +/* \internal + convert a signal index from the method range to the signal range + */ +static int methodIndexToSignalIndex(const QMetaObject *metaObject, int signal_index) +{ + if (signal_index < 0) + return signal_index; + while (metaObject && metaObject->methodOffset() > signal_index) + metaObject = metaObject->superClass(); + + if (metaObject) { + int signalOffset, methodOffset; + computeOffsets(metaObject, &signalOffset, &methodOffset); + if (signal_index < metaObject->methodCount()) + signal_index = QMetaObjectPrivate::originalClone(metaObject, signal_index - methodOffset) + signalOffset; + else + signal_index = signal_index - methodOffset + signalOffset; + } + return signal_index; +} + /*!\internal \a types is a 0-terminated vector of meta types for queued connections. @@ -2860,16 +2881,7 @@ void QObject::disconnectNotify(const char *) bool QMetaObject::connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type, int *types) { - if (signal_index > 0) { - const QMetaObject *mo = sender->metaObject(); - while (mo && mo->methodOffset() > signal_index) - mo = mo->superClass(); - if (mo) { - int signalOffset, methodOffset; - computeOffsets(mo, &signalOffset, &methodOffset); - signal_index = QMetaObjectPrivate::originalClone(mo, signal_index - methodOffset) + signalOffset; - } - } + signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index); return QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types); } @@ -2938,16 +2950,7 @@ bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, bool QMetaObject::disconnect(const QObject *sender, int signal_index, const QObject *receiver, int method_index) { - if (signal_index > 0) { - const QMetaObject *mo = sender->metaObject(); - while (mo && mo->methodOffset() > signal_index) - mo = mo->superClass(); - if (mo) { - int signalOffset, methodOffset; - computeOffsets(mo, &signalOffset, &methodOffset); - signal_index = QMetaObjectPrivate::originalClone(mo, signal_index - methodOffset) + signalOffset; - } - } + signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index); return QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index); } diff --git a/src/gui/effects/qgraphicseffect.cpp b/src/gui/effects/qgraphicseffect.cpp index d6cabaa..a523bab 100644 --- a/src/gui/effects/qgraphicseffect.cpp +++ b/src/gui/effects/qgraphicseffect.cpp @@ -313,7 +313,10 @@ QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offse // Shortcut, no cache for childless pixmap items... const QGraphicsItem *item = graphicsItem(); if (system == Qt::LogicalCoordinates && mode == QGraphicsEffect::NoPad && item && isPixmap()) { - return ((QGraphicsPixmapItem *) item)->pixmap(); + const QGraphicsPixmapItem *pixmapItem = static_cast<const QGraphicsPixmapItem *>(item); + if (offset) + *offset = pixmapItem->offset().toPoint(); + return pixmapItem->pixmap(); } if (system == Qt::DeviceCoordinates && item @@ -371,10 +374,14 @@ QGraphicsEffectSourcePrivate::~QGraphicsEffectSourcePrivate() invalidateCache(); } -void QGraphicsEffectSourcePrivate::invalidateCache(bool effectRectChanged) const +void QGraphicsEffectSourcePrivate::invalidateCache(InvalidateReason reason) const { - if (effectRectChanged && m_cachedMode != QGraphicsEffect::PadToEffectiveBoundingRect) + if (m_cachedMode != QGraphicsEffect::PadToEffectiveBoundingRect + && (reason == EffectRectChanged + || reason == TransformChanged + && m_cachedSystem == Qt::LogicalCoordinates)) return; + QPixmapCache::remove(m_cacheKey); } @@ -520,7 +527,7 @@ void QGraphicsEffect::updateBoundingRect() Q_D(QGraphicsEffect); if (d->source) { d->source->d_func()->effectBoundingRectChanged(); - d->source->d_func()->invalidateCache(true); + d->source->d_func()->invalidateCache(QGraphicsEffectSourcePrivate::EffectRectChanged); } } @@ -837,22 +844,19 @@ QRectF QGraphicsBlurEffect::boundingRectFor(const QRectF &rect) const void QGraphicsBlurEffect::draw(QPainter *painter) { Q_D(QGraphicsBlurEffect); - if (d->filter->radius() <= 0) { + if (d->filter->radius() < 1) { drawSource(painter); return; } PixmapPadMode mode = PadToEffectiveBoundingRect; if (painter->paintEngine()->type() == QPaintEngine::OpenGL2) - mode = PadToTransparentBorder; + mode = NoPad; // Draw pixmap in device coordinates to avoid pixmap scaling. QPoint offset; - const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, mode); - QTransform restoreTransform = painter->worldTransform(); - painter->setWorldTransform(QTransform()); + QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, mode); d->filter->draw(painter, offset, pixmap); - painter->setWorldTransform(restoreTransform); } /*! @@ -1033,7 +1037,7 @@ void QGraphicsDropShadowEffect::draw(QPainter *painter) PixmapPadMode mode = PadToEffectiveBoundingRect; if (painter->paintEngine()->type() == QPaintEngine::OpenGL2) - mode = PadToTransparentBorder; + mode = NoPad; // Draw pixmap in device coordinates to avoid pixmap scaling. QPoint offset; diff --git a/src/gui/effects/qgraphicseffect_p.h b/src/gui/effects/qgraphicseffect_p.h index 0011eef..cab7a48 100644 --- a/src/gui/effects/qgraphicseffect_p.h +++ b/src/gui/effects/qgraphicseffect_p.h @@ -108,6 +108,13 @@ public: , m_cachedMode(QGraphicsEffect::PadToTransparentBorder) {} + enum InvalidateReason + { + TransformChanged, + EffectRectChanged, + SourceChanged + }; + virtual ~QGraphicsEffectSourcePrivate(); virtual void detach() = 0; virtual QRectF boundingRect(Qt::CoordinateSystem system) const = 0; @@ -121,7 +128,9 @@ public: virtual QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset = 0, QGraphicsEffect::PixmapPadMode mode = QGraphicsEffect::PadToTransparentBorder) const = 0; virtual void effectBoundingRectChanged() = 0; - void invalidateCache(bool effectRectChanged = false) const; + + void invalidateCache(InvalidateReason reason = SourceChanged) const; + Qt::CoordinateSystem currentCachedSystem() const { return m_cachedSystem; } friend class QGraphicsScenePrivate; friend class QGraphicsItem; diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index d955f16..fbfd8e6 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -7177,9 +7177,16 @@ void QGraphicsItem::prepareGeometryChange() QGraphicsItem *parent = this; while ((parent = parent->d_ptr->parent)) { - parent->d_ptr->dirtyChildrenBoundingRect = 1; + QGraphicsItemPrivate *parentp = parent->d_ptr.data(); + parentp->dirtyChildrenBoundingRect = 1; // ### Only do this if the parent's effect applies to the entire subtree. - parent->d_ptr->notifyBoundingRectChanged = 1; + parentp->notifyBoundingRectChanged = 1; +#ifndef QT_NO_GRAPHICSEFFECT + if (parentp->scene && parentp->graphicsEffect) { + parentp->notifyInvalidated = 1; + static_cast<QGraphicsItemEffectSourcePrivate *>(parentp->graphicsEffect->d_func()->source->d_func())->invalidateCache(); + } +#endif } } @@ -10717,7 +10724,6 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context"); return QPixmap(); } - if (!item->d_ptr->scene) return QPixmap(); QGraphicsScenePrivate *scened = item->d_ptr->scene->d_func(); @@ -10725,9 +10731,11 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP const QRectF sourceRect = boundingRect(system); QRectF effectRectF; + bool unpadded = false; if (mode == QGraphicsEffect::PadToEffectiveBoundingRect) { if (info) { effectRectF = item->graphicsEffect()->boundingRectFor(boundingRect(Qt::DeviceCoordinates)); + unpadded = (effectRectF.size() == sourceRect.size()); if (info && system == Qt::LogicalCoordinates) effectRectF = info->painter->worldTransform().inverted().mapRect(effectRectF); } else { @@ -10739,6 +10747,7 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP effectRectF = sourceRect.adjusted(-1.5, -1.5, 1.5, 1.5); } else { effectRectF = sourceRect; + unpadded = true; } QRect effectRect = effectRectF.toAlignedRect(); @@ -10746,6 +10755,14 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP if (offset) *offset = effectRect.topLeft(); + bool untransformed = !deviceCoordinates + || info->painter->worldTransform().type() <= QTransform::TxTranslate; + if (untransformed && unpadded && isPixmap()) { + if (offset) + *offset = boundingRect(system).topLeft().toPoint(); + return static_cast<QGraphicsPixmapItem *>(item)->pixmap(); + } + if (deviceCoordinates) { // Clip to viewport rect. int left, top, right, bottom; @@ -10773,12 +10790,6 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP if (effectRect.isEmpty()) return QPixmap(); - if (system == Qt::LogicalCoordinates - && effectRect.size() == sourceRect.size() - && isPixmap()) { - return static_cast<QGraphicsPixmapItem *>(item)->pixmap(); - } - QPixmap pixmap(effectRect.size()); pixmap.fill(Qt::transparent); QPainter pixmapPainter(&pixmap); diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 6176f2d..182a3f5 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -4671,10 +4671,13 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * painter->setWorldTransform(*transformPtr); painter->setOpacity(opacity); - if (sourced->lastEffectTransform != painter->worldTransform()) { + if (sourced->currentCachedSystem() != Qt::LogicalCoordinates + && sourced->lastEffectTransform != painter->worldTransform()) + { sourced->lastEffectTransform = painter->worldTransform(); - sourced->invalidateCache(); + sourced->invalidateCache(QGraphicsEffectSourcePrivate::TransformChanged); } + item->d_ptr->graphicsEffect->draw(painter); painter->setWorldTransform(restoreTransform); sourced->info = 0; diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp index 3723500..caa0752 100644 --- a/src/gui/image/qpixmapfilter.cpp +++ b/src/gui/image/qpixmapfilter.cpp @@ -52,6 +52,10 @@ #include "private/qgraphicssystem_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 @@ -585,106 +589,338 @@ QGraphicsBlurEffect::BlurHints QPixmapBlurFilter::blurHints() const return d->hints; } +const qreal radiusScale = qreal(2.5); + /*! \internal */ QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const { Q_D(const QPixmapBlurFilter); - const qreal delta = d->radius + 1; + const qreal delta = radiusScale * d->radius + 1; 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, bool alphaOnly = false) +template <int shift> +static inline int static_shift(int value) { - 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]; + if (shift == 0) + return value; + else if (shift > 0) + return value << (uint(shift) & 0x1f); + else + return value >> (uint(-shift) & 0x1f); +} - QImage result = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); - int r1 = rect.top(); - int r2 = rect.bottom(); - int c1 = rect.left(); - int c2 = rect.right(); +template<int aprec, int zprec> +static inline void 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 = static_shift<zprec - 24>(*pixel) & Z_MASK; + const int R_zprec = static_shift<zprec - 16>(*pixel) & Z_MASK; + const int G_zprec = static_shift<zprec - 8>(*pixel) & Z_MASK; + const int B_zprec = 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 = + static_shift<24 - zprec - aprec>(zA & ZA_MASK) + | static_shift<16 - zprec - aprec>(zR & ZA_MASK) + | static_shift<8 - zprec - aprec>(zG & ZA_MASK) + | static_shift<-zprec - aprec>(zB & ZA_MASK); +#undef ZA_MASK +} - int bpl = result.bytesPerLine(); - int rgba[4]; - unsigned char* p; +const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3); - int i1 = 0; - int i2 = 3; +template<int aprec, int zprec> +static inline void 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); +} - if (alphaOnly) - i1 = i2 = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3); +template<int aprec, int zprec, bool alphaOnly> +static inline void 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) + blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha); + else + 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) + blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha); + else + 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); + + // 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 <= improvedQuality; ++i) + 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()); + } + } - for (int col = c1; col <= c2; col++) { - p = result.scanLine(r1) + col * 4; - for (int i = i1; i <= i2; i++) - rgba[i] = p[i] << 4; + img_height = temp.height(); + for (int row = 0; row < img_height; ++row) { + for (int i = 0; i <= improvedQuality; ++i) + blurrow<aprec, zprec, alphaOnly>(temp, row, alpha); + } - p += bpl; - for (int j = r1; j < r2; j++, p += bpl) - for (int i = i1; i <= i2; i++) - p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + if (transposed == 0) { + 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)) ) - for (int row = r1; row <= r2; row++) { - p = result.scanLine(row) + c1 * 4; - for (int i = i1; i <= i2; i++) - rgba[i] = p[i] << 4; +Q_GUI_EXPORT QImage qt_halfScaled(const QImage &source) +{ + 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; + } + } - p += 4; - for (int j = c1; j < c2; j++, p += 4) - for (int i = i1; i <= i2; i++) - p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + return dest; + } else if (source.format() != QImage::Format_ARGB32_Premultiplied + && source.format() != QImage::Format_RGB32) + { + srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied); } - for (int col = c1; col <= c2; col++) { - p = result.scanLine(r2) + col * 4; - for (int i = i1; i <= i2; i++) - rgba[i] = p[i] << 4; + 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(); - p -= bpl; - for (int j = r1; j < r2; j++, p -= bpl) - for (int i = i1; i <= i2; i++) - p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + 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])); } - for (int row = r1; row <= r2; row++) { - p = result.scanLine(row) + c2 * 4; - for (int i = i1; i <= i2; i++) - rgba[i] = p[i] << 4; + return dest; +} - p -= 4; - for (int j = c1; j < c2; j++, p -= 4) - for (int i = i1; i <= i2; i++) - p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; +Q_GUI_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); } - return result; + qreal scale = 1; + if (radius >= 4) { + 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_GUI_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); } +bool qt_scaleForTransform(const QTransform &transform, qreal *scale); + /*! \internal */ -void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &srcRect) const +void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &rect) const { Q_D(const QPixmapBlurFilter); if (!painter->isActive()) return; - if (d->radius <= 0) { + 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(d->radius); + blurFilter->setRadius(scaledRadius); blurFilter->setBlurHints(d->hints); blurFilter->draw(painter, p, src, srcRect); return; @@ -693,17 +929,17 @@ void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap QImage srcImage; QImage destImage; - if (srcRect.isNull()) { + if (srcRect == src.rect()) { srcImage = src.toImage(); - destImage = blurred(srcImage, srcImage.rect(), qRound(d->radius)); } else { QRect rect = srcRect.toAlignedRect().intersected(src.rect()); - srcImage = src.copy(rect).toImage(); - destImage = blurred(srcImage, srcImage.rect(), qRound(d->radius)); } - painter->drawImage(p, destImage); + 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 @@ -1095,7 +1331,13 @@ void QPixmapDropShadowFilter::draw(QPainter *p, tmpPainter.end(); // blur the alpha channel - tmp = blurred(tmp, tmp.rect(), qRound(d->radius), true); + 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); diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 5ddba06..f447989 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -70,6 +70,7 @@ QAbstractItemViewPrivate::QAbstractItemViewPrivate() itemDelegate(0), selectionModel(0), ctrlDragSelectionFlag(QItemSelectionModel::NoUpdate), + noSelectionOnMousePress(false), selectionMode(QAbstractItemView::ExtendedSelection), selectionBehavior(QAbstractItemView::SelectItems), currentlyCommittingEditor(0), @@ -1622,6 +1623,7 @@ void QAbstractItemView::mousePressEvent(QMouseEvent *event) d->pressedIndex = index; d->pressedModifiers = event->modifiers(); QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); + d->noSelectionOnMousePress = command == QItemSelectionModel::NoUpdate || !index.isValid(); QPoint offset = d->offset(); if ((command & QItemSelectionModel::Current) == 0) d->pressedPosition = pos + offset; @@ -1760,9 +1762,10 @@ void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event) d->ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate; - //in the case the user presses on no item we might decide to clear the selection - if (d->selectionModel && !index.isValid()) - d->selectionModel->select(QModelIndex(), selectionCommand(index, event)); + if (d->selectionModel && d->noSelectionOnMousePress) { + d->noSelectionOnMousePress = false; + d->selectionModel->select(index, selectionCommand(index, event)); + } setState(NoState); diff --git a/src/gui/itemviews/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h index 0b5cfbe..7fc6780 100644 --- a/src/gui/itemviews/qabstractitemview_p.h +++ b/src/gui/itemviews/qabstractitemview_p.h @@ -347,6 +347,7 @@ public: QMap<int, QPointer<QAbstractItemDelegate> > columnDelegates; QPointer<QItemSelectionModel> selectionModel; QItemSelectionModel::SelectionFlag ctrlDragSelectionFlag; + bool noSelectionOnMousePress; QAbstractItemView::SelectionMode selectionMode; QAbstractItemView::SelectionBehavior selectionBehavior; diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp index 67dc2c7..e3a6f78 100644 --- a/src/gui/painting/qmemrotate.cpp +++ b/src/gui/painting/qmemrotate.cpp @@ -572,5 +572,26 @@ QT_IMPL_MEMROTATE(quint32, qrgb_generic16) QT_IMPL_MEMROTATE(quint16, qrgb_generic16) #endif +struct qrgb_gl_rgba +{ +public: + inline qrgb_gl_rgba(quint32 v) { + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) + data = ((v << 16) & 0xff0000) | ((v >> 16) & 0xff) | (v & 0xff00ff00); + else + data = (v << 8) | ((v >> 24) & 0xff); + } + + inline operator quint32() const { return data; } + +private: + quint32 data; +} Q_PACKED; + +void Q_GUI_EXPORT qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHeight, int srcStride, + quint32 *dest, int dstStride) +{ + qt_memrotate90_template(src, srcWidth, srcHeight, srcStride, reinterpret_cast<qrgb_gl_rgba *>(dest), dstStride); +} QT_END_NAMESPACE diff --git a/src/gui/painting/qmemrotate_p.h b/src/gui/painting/qmemrotate_p.h index 676a880..8aee575 100644 --- a/src/gui/painting/qmemrotate_p.h +++ b/src/gui/painting/qmemrotate_p.h @@ -81,6 +81,8 @@ QT_BEGIN_NAMESPACE void Q_GUI_QWS_EXPORT qt_memrotate180(const srctype*, int, int, int, desttype*, int); \ void Q_GUI_QWS_EXPORT qt_memrotate270(const srctype*, int, int, int, desttype*, int) +void Q_GUI_EXPORT qt_memrotate90(const quint32*, int, int, int, quint32*, int); + QT_DECL_MEMROTATE(quint32, quint32); QT_DECL_MEMROTATE(quint32, quint16); QT_DECL_MEMROTATE(quint16, quint32); diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index a4e7c04..a75d70f 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -404,7 +404,9 @@ QFixed QCoreTextFontEngine::ascent() const } QFixed QCoreTextFontEngine::descent() const { - return QFixed::fromReal(CTFontGetDescent(ctfont)).ceil(); + // subtract a pixel to even out the historical +1 in QFontMetrics::height(). + // Fix in Qt 5. + return QFixed::fromReal(CTFontGetDescent(ctfont)).ceil() - 1; } QFixed QCoreTextFontEngine::leading() const { @@ -1406,7 +1408,9 @@ QFixed QFontEngineMac::ascent() const QFixed QFontEngineMac::descent() const { - return m_descent; + // subtract a pixel to even out the historical +1 in QFontMetrics::height(). + // Fix in Qt 5. + return m_descent - 1; } QFixed QFontEngineMac::leading() const diff --git a/src/gui/widgets/qplaintextedit.cpp b/src/gui/widgets/qplaintextedit.cpp index f79931a..5e7d06e 100644 --- a/src/gui/widgets/qplaintextedit.cpp +++ b/src/gui/widgets/qplaintextedit.cpp @@ -684,8 +684,12 @@ void QPlainTextEditPrivate::ensureVisible(int position, bool center, bool forceC qreal h = center ? line.naturalTextRect().center().y() : line.naturalTextRect().bottom(); + QTextBlock previousVisibleBlock = block; while (h < height && block.previous().isValid()) { - block = block.previous(); + previousVisibleBlock = block; + do { + block = block.previous(); + } while (!block.isVisible() && block.previous().isValid()); h += q->blockBoundingRect(block).height(); } @@ -699,8 +703,8 @@ void QPlainTextEditPrivate::ensureVisible(int position, bool center, bool forceC ++l; } - if (block.next().isValid() && l >= lineCount) { - block = block.next(); + if (l >= lineCount) { + block = previousVisibleBlock; l = 0; } setTopBlock(block.blockNumber(), l); diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h index e319389..e0033be 100644 --- a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h +++ b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h @@ -68,7 +68,7 @@ class Q_OPENGL_EXPORT QGLCustomShaderStage public: QGLCustomShaderStage(); virtual ~QGLCustomShaderStage(); - virtual void setUniforms(QGLShaderProgram*) = 0; + virtual void setUniforms(QGLShaderProgram*) {} void setUniformsDirty(); diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 0084476..77ca3a8 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -291,10 +291,7 @@ public: QScopedPointer<QPixmapFilter> convolutionFilter; QScopedPointer<QPixmapFilter> colorizeFilter; QScopedPointer<QPixmapFilter> blurFilter; - QScopedPointer<QPixmapFilter> animationBlurFilter; - QScopedPointer<QPixmapFilter> fastBlurFilter; QScopedPointer<QPixmapFilter> dropShadowFilter; - QScopedPointer<QPixmapFilter> fastDropShadowFilter; QSet<QVectorPath::CacheEntry *> pathCaches; QVector<GLuint> unusedVBOSToClean; diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index b6f8919..32534aa 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2063,6 +2063,29 @@ QGLTexture *QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G // #define QGL_BIND_TEXTURE_DEBUG +// map from Qt's ARGB endianness-dependent format to GL's big-endian RGBA layout +static inline void qgl_byteSwapImage(QImage &img, GLenum pixel_type) +{ + const int width = img.width(); + const int height = img.height(); + + if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV + || (pixel_type == GL_UNSIGNED_BYTE && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) + { + for (int i = 0; i < height; ++i) { + uint *p = (uint *) img.scanLine(i); + for (int x = 0; x < width; ++x) + p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); + } + } else { + for (int i = 0; i < height; ++i) { + uint *p = (uint *) img.scanLine(i); + for (int x = 0; x < width; ++x) + p[x] = (p[x] << 8) | ((p[x] >> 24) & 0xff); + } + } +} + QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint internalFormat, const qint64 key, QGLContext::BindOptions options) { @@ -2215,23 +2238,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G // 32 in the switch above is for the RGB16 case, where we set // the format to GL_RGB Q_ASSERT(img.depth() == 32); - const int width = img.width(); - const int height = img.height(); - - if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV - || (pixel_type == GL_UNSIGNED_BYTE && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) { - for (int i=0; i < height; ++i) { - uint *p = (uint *) img.scanLine(i); - for (int x=0; x<width; ++x) - p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); - } - } else { - for (int i=0; i < height; ++i) { - uint *p = (uint *) img.scanLine(i); - for (int x=0; x<width; ++x) - p[x] = (p[x] << 8) | ((p[x] >> 24) & 0xff); - } - } + qgl_byteSwapImage(img, pixel_type); } #ifdef QT_OPENGL_ES // OpenGL/ES requires that the internal and external formats be identical. @@ -3803,6 +3810,11 @@ bool QGLWidget::event(QEvent *e) } #if defined(QT_OPENGL_ES) + // A re-parent is likely to destroy the X11 window and re-create it. It is important + // that we free the EGL surface _before_ the winID changes - otherwise we can leak. + if (e->type() == QEvent::ParentAboutToChange) + d->glcx->d_func()->destroyEglSurfaceForDevice(); + if ((e->type() == QEvent::ParentChange) || (e->type() == QEvent::WindowStateChange)) { // The window may have been re-created during re-parent or state change - if so, the EGL // surface will need to be re-created. diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index 079953f..2076c46 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -546,6 +546,7 @@ private: friend class QGLPixelBuffer; friend class QGLPixelBufferPrivate; friend class QGLContext; + friend class QGLContextPrivate; friend class QGLOverlayWidget; friend class QOpenGLPaintEngine; friend class QGLPaintDevice; diff --git a/src/opengl/qgl_egl.cpp b/src/opengl/qgl_egl.cpp index fbf0349..839e8eb 100644 --- a/src/opengl/qgl_egl.cpp +++ b/src/opengl/qgl_egl.cpp @@ -142,19 +142,7 @@ void QGLContext::reset() d->cleanup(); doneCurrent(); if (d->eglContext) { - if (d->eglSurface != EGL_NO_SURFACE) { -#ifdef Q_WS_X11 - // Make sure we don't call eglDestroySurface on a surface which - // was created for a different winId: - if (d->paintDevice->devType() == QInternal::Widget) { - QGLWidget* w = static_cast<QGLWidget*>(d->paintDevice); - - if (w->d_func()->eglSurfaceWindowId == w->winId()) - eglDestroySurface(d->eglContext->display(), d->eglSurface); - } else -#endif - eglDestroySurface(d->eglContext->display(), d->eglSurface); - } + d->destroyEglSurfaceForDevice(); delete d->eglContext; } d->eglContext = 0; @@ -198,6 +186,26 @@ void QGLContext::swapBuffers() const d->eglContext->swapBuffers(d->eglSurface); } +void QGLContextPrivate::destroyEglSurfaceForDevice() +{ + if (eglSurface != EGL_NO_SURFACE) { +#ifdef Q_WS_X11 + // Make sure we don't call eglDestroySurface on a surface which + // was created for a different winId: + if (paintDevice->devType() == QInternal::Widget) { + QGLWidget* w = static_cast<QGLWidget*>(paintDevice); + + if (w->d_func()->eglSurfaceWindowId == w->winId()) + eglDestroySurface(eglContext->display(), eglSurface); + else + qWarning("WARNING: Potential EGL surface leak!"); + } else +#endif + eglDestroySurface(eglContext->display(), eglSurface); + eglSurface = EGL_NO_SURFACE; + } +} + void QGLWidget::setMouseTracking(bool enable) { QWidget::setMouseTracking(enable); diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 11770d3..99c0f33 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -288,6 +288,7 @@ public: #if defined(QT_OPENGL_ES) QEglContext *eglContext; EGLSurface eglSurface; + void destroyEglSurfaceForDevice(); #elif defined(Q_WS_X11) || defined(Q_WS_MAC) void* cx; #endif diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index a868e83..19026b3 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -173,10 +173,16 @@ void QGLWidget::updateOverlayGL() //handle overlay } +//#define QT_DEBUG_X11_VISUAL_SELECTION 1 + bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig config, const QX11Info &x11Info, bool useArgbVisual) { bool foundVisualIsArgb = useArgbVisual; +#ifdef QT_DEBUG_X11_VISUAL_SELECTION + qDebug("qt_egl_setup_x11_visual() - useArgbVisual=%d", useArgbVisual); +#endif + memset(&vi, 0, sizeof(XVisualInfo)); EGLint eglConfigColorSize; @@ -199,7 +205,9 @@ bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig conf XRenderPictFormat *format; format = XRenderFindVisualFormat(x11Info.display(), chosenVisualInfo->visual); if (format->type == PictTypeDirect && format->direct.alphaMask) { -// qDebug("Using ARGB X Visual ID (%d) provided by EGL", (int)vi.visualid); +#ifdef QT_DEBUG_X11_VISUAL_SELECTION + qDebug("Using ARGB X Visual ID (%d) provided by EGL", (int)vi.visualid); +#endif foundVisualIsArgb = true; vi = *chosenVisualInfo; } @@ -212,7 +220,9 @@ bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig conf #endif { if (eglConfigColorSize == chosenVisualInfo->depth) { -// qDebug("Using opaque X Visual ID (%d) provided by EGL", (int)vi.visualid); +#ifdef QT_DEBUG_X11_VISUAL_SELECTION + qDebug("Using opaque X Visual ID (%d) provided by EGL", (int)vi.visualid); +#endif vi = *chosenVisualInfo; } else qWarning("Warning: EGL suggested using X visual ID %d (%d bpp) for config %d (%d bpp), but the depths do not match!", @@ -248,7 +258,9 @@ bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig conf if (format->type == PictTypeDirect && format->direct.alphaMask) { vi = matchingVisuals[i]; foundVisualIsArgb = true; -// qDebug("Using X Visual ID (%d) for ARGB visual as provided by XRender", (int)vi.visualid); +#ifdef QT_DEBUG_X11_VISUAL_SELECTION + qDebug("Using X Visual ID (%d) for ARGB visual as provided by XRender", (int)vi.visualid); +#endif break; } } @@ -272,24 +284,28 @@ bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig conf } else qWarning(" - Falling back to X11 suggested depth (%d)", depth); } -// else -// qDebug("Using X Visual ID (%d) for EGL provided depth (%d)", (int)vi.visualid, depth); +#ifdef QT_DEBUG_X11_VISUAL_SELECTION + else + qDebug("Using X Visual ID (%d) for EGL provided depth (%d)", (int)vi.visualid, depth); +#endif // Don't try to use ARGB now unless the visual is 32-bit - even then it might stil fail :-( if (useArgbVisual) foundVisualIsArgb = vi.depth == 32; //### We might at some point (soon) get ARGB4444 } -// qDebug("Visual Info:"); -// qDebug(" bits_per_rgb=%d", vi.bits_per_rgb); -// qDebug(" red_mask=0x%x", vi.red_mask); -// qDebug(" green_mask=0x%x", vi.green_mask); -// qDebug(" blue_mask=0x%x", vi.blue_mask); -// qDebug(" colormap_size=%d", vi.colormap_size); -// qDebug(" c_class=%d", vi.c_class); -// qDebug(" depth=%d", vi.depth); -// qDebug(" screen=%d", vi.screen); -// qDebug(" visualid=%d", vi.visualid); +#ifdef QT_DEBUG_X11_VISUAL_SELECTION + qDebug("Visual Info:"); + qDebug(" bits_per_rgb=%d", vi.bits_per_rgb); + qDebug(" red_mask=0x%x", vi.red_mask); + qDebug(" green_mask=0x%x", vi.green_mask); + qDebug(" blue_mask=0x%x", vi.blue_mask); + qDebug(" colormap_size=%d", vi.colormap_size); + qDebug(" c_class=%d", vi.c_class); + qDebug(" depth=%d", vi.depth); + qDebug(" screen=%d", vi.screen); + qDebug(" visualid=%d", vi.visualid); +#endif return foundVisualIsArgb; } @@ -320,7 +336,7 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, // If the application has set WA_TranslucentBackground and not explicitly set // the alpha buffer size to zero, modify the format so it have an alpha channel QGLFormat& fmt = d->glcx->d_func()->glFormat; - const bool tryArgbVisual = testAttribute(Qt::WA_TranslucentBackground); + const bool tryArgbVisual = testAttribute(Qt::WA_TranslucentBackground) || fmt.alpha(); if (tryArgbVisual && fmt.alphaBufferSize() == -1) fmt.setAlphaBufferSize(1); diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index c478630..d4bcbe9 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -55,11 +55,17 @@ #include "qgl_p.h" #include "private/qapplication_p.h" +#include "private/qdrawhelper_p.h" +#include "private/qmemrotate_p.h" #include "private/qmath_p.h" #include "qmath.h" QT_BEGIN_NAMESPACE +// qpixmapfilter.cpp +void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0); +QImage qt_halfScaled(const QImage &source); + void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const { const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, QGLContext::BindOptions(QGLContext::DefaultBindOption | QGLContext::MemoryManagedBindOption)); @@ -102,48 +108,21 @@ private: class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter> { public: - QGLPixmapBlurFilter(QGraphicsBlurEffect::BlurHints hints); - - void setUniforms(QGLShaderProgram *program); - - static QByteArray generateGaussianShader(int radius, bool singlePass = false, bool dropShadow = false); + QGLPixmapBlurFilter(); protected: bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; - -private: - - mutable QSize m_textureSize; - mutable bool m_horizontalBlur; - mutable bool m_singlePass; - mutable bool m_animatedBlur; - - mutable qreal m_t; - mutable QSize m_targetSize; - - mutable bool m_haveCached; - mutable int m_cachedRadius; - mutable QGraphicsBlurEffect::BlurHints m_hints; }; class QGLPixmapDropShadowFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapDropShadowFilter> { public: - QGLPixmapDropShadowFilter(QGraphicsBlurEffect::BlurHints hints); + QGLPixmapDropShadowFilter(); void setUniforms(QGLShaderProgram *program); protected: bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; - -private: - mutable QSize m_textureSize; - mutable bool m_horizontalBlur; - mutable bool m_singlePass; - - mutable bool m_haveCached; - mutable int m_cachedRadius; - mutable QGraphicsBlurEffect::BlurHints m_hints; }; extern QGLWidget *qt_gl_share_widget(); @@ -158,31 +137,14 @@ QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *pr return d->colorizeFilter.data(); case QPixmapFilter::BlurFilter: { - const QPixmapBlurFilter *proto = static_cast<const QPixmapBlurFilter *>(prototype); - if (proto->blurHints() & QGraphicsBlurEffect::AnimationHint) { - if (!d->animationBlurFilter) - d->animationBlurFilter.reset(new QGLPixmapBlurFilter(proto->blurHints())); - return d->animationBlurFilter.data(); - } - if ((proto->blurHints() & QGraphicsBlurEffect::QualityHint) && proto->radius() > 5) { - if (!d->blurFilter) - d->blurFilter.reset(new QGLPixmapBlurFilter(QGraphicsBlurEffect::QualityHint)); - return d->blurFilter.data(); - } - if (!d->fastBlurFilter) - d->fastBlurFilter.reset(new QGLPixmapBlurFilter(QGraphicsBlurEffect::PerformanceHint)); - return d->fastBlurFilter.data(); + if (!d->blurFilter) + d->blurFilter.reset(new QGLPixmapBlurFilter()); + return d->blurFilter.data(); } case QPixmapFilter::DropShadowFilter: { - const QPixmapDropShadowFilter *proto = static_cast<const QPixmapDropShadowFilter *>(prototype); - if (proto->blurRadius() <= 5) { - if (!d->fastDropShadowFilter) - d->fastDropShadowFilter.reset(new QGLPixmapDropShadowFilter(QGraphicsBlurEffect::PerformanceHint)); - return d->fastDropShadowFilter.data(); - } if (!d->dropShadowFilter) - d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter(QGraphicsBlurEffect::QualityHint)); + d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter()); return d->dropShadowFilter.data(); } @@ -311,59 +273,43 @@ bool QGLPixmapConvolutionFilter::processGL(QPainter *painter, const QPointF &pos return true; } -static const char *qt_gl_texture_sampling_helper = - "lowp float texture2DAlpha(lowp sampler2D src, highp vec2 srcCoords) {\n" - " return texture2D(src, srcCoords).a;\n" - "}\n"; - -QGLPixmapBlurFilter::QGLPixmapBlurFilter(QGraphicsBlurEffect::BlurHints hints) - : m_animatedBlur(false) - , m_haveCached(false) - , m_cachedRadius(0) - , m_hints(hints) -{ -} - -// should be even numbers as they will be divided by two -static const int qCachedBlurLevels[] = { 6, 14, 30 }; -static const int qNumCachedBlurTextures = sizeof(qCachedBlurLevels) / sizeof(*qCachedBlurLevels); -static const int qMaxCachedBlurLevel = qCachedBlurLevels[qNumCachedBlurTextures - 1]; - -static qreal qLogBlurLevel(int level) +QGLPixmapBlurFilter::QGLPixmapBlurFilter() { - static bool initialized = false; - static qreal logBlurLevelCache[qNumCachedBlurTextures]; - if (!initialized) { - for (int i = 0; i < qNumCachedBlurTextures; ++i) - logBlurLevelCache[i] = qLn(qCachedBlurLevels[i]); - initialized = true; - } - return logBlurLevelCache[level]; } class QGLBlurTextureInfo { public: - QGLBlurTextureInfo(QSize size, GLuint textureIds[]) - : m_size(size) + QGLBlurTextureInfo(const QImage &image, GLuint tex, qreal r) + : m_texture(tex) + , m_radius(r) { - for (int i = 0; i < qNumCachedBlurTextures; ++i) - m_textureIds[i] = textureIds[i]; + m_paddedImage << image; } ~QGLBlurTextureInfo() { - glDeleteTextures(qNumCachedBlurTextures, m_textureIds); + glDeleteTextures(1, &m_texture); } - QSize size() const { return m_size; } - GLuint textureId(int i) const { return m_textureIds[i]; } + QImage paddedImage(int scaleLevel = 0) const; + GLuint texture() const { return m_texture; } + qreal radius() const { return m_radius; } private: - GLuint m_textureIds[qNumCachedBlurTextures]; - QSize m_size; + mutable QList<QImage> m_paddedImage; + GLuint m_texture; + qreal m_radius; }; +QImage QGLBlurTextureInfo::paddedImage(int scaleLevel) const +{ + for (int i = m_paddedImage.size() - 1; i <= scaleLevel; ++i) + m_paddedImage << qt_halfScaled(m_paddedImage.at(i)); + + return m_paddedImage.at(scaleLevel); +} + class QGLBlurTextureCache : public QObject { public: @@ -373,7 +319,6 @@ public: ~QGLBlurTextureCache(); QGLBlurTextureInfo *takeBlurTextureInfo(const QPixmap &pixmap); - bool fitsInCache(const QPixmap &pixmap) const; bool hasBlurTextureInfo(const QPixmap &pixmap) const; void insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTextureInfo *info); void clearBlurTextureInfo(const QPixmap &pixmap); @@ -458,12 +403,7 @@ void QGLBlurTextureCache::insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTe if (timerId) killTimer(timerId); - timerId = startTimer(1000); -} - -bool QGLBlurTextureCache::fitsInCache(const QPixmap &pixmap) const -{ - return pixmap.width() * pixmap.height() <= cache.maxCost(); + timerId = startTimer(8000); } void QGLBlurTextureCache::pixmapDestroyed(QPixmap *pixmap) @@ -474,567 +414,217 @@ void QGLBlurTextureCache::pixmapDestroyed(QPixmap *pixmap) } } -static const char *qt_gl_interpolate_filter = - "uniform lowp float interpolationValue;" - "uniform lowp sampler2D interpolateTarget;" - "uniform highp vec4 interpolateMapping;" - "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords)" - "{" - " return mix(texture2D(interpolateTarget, interpolateMapping.xy + interpolateMapping.zw * srcCoords)," - " texture2D(src, srcCoords), interpolationValue);" - "}"; +static const int qAnimatedBlurLevelIncrement = 16; +static const int qMaxBlurHalfScaleLevel = 1; -static void initializeTexture(GLuint id, int width, int height) +static GLuint generateBlurTexture(const QSize &size, GLenum format = GL_RGBA) { - glBindTexture(GL_TEXTURE_2D, id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindTexture(GL_TEXTURE_2D, 0); + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0, format, + GL_UNSIGNED_BYTE, 0); + return texture; } -bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const +static inline uint nextMultiple(uint x, uint multiplier) { - QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this); - - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx); - - if ((m_hints & QGraphicsBlurEffect::AnimationHint) && blurTextureCache->fitsInCache(src)) { - QRect targetRect = src.rect().adjusted(-qMaxCachedBlurLevel, -qMaxCachedBlurLevel, qMaxCachedBlurLevel, qMaxCachedBlurLevel); - // ensure even dimensions (going to divide by two) - targetRect.setWidth((targetRect.width() + 1) & ~1); - targetRect.setHeight((targetRect.height() + 1) & ~1); - - QGLBlurTextureInfo *info = 0; - if (blurTextureCache->hasBlurTextureInfo(src)) { - info = blurTextureCache->takeBlurTextureInfo(src); - } else { - m_animatedBlur = false; - m_hints = QGraphicsBlurEffect::QualityHint; - m_singlePass = false; - - QGLFramebufferObjectFormat format; - format.setInternalTextureFormat(GLenum(GL_RGBA)); - QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(targetRect.size() / 2, format, true); - - if (!fbo) - return false; - - QPainter fboPainter(fbo); - QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(fboPainter.paintEngine()); - - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); - - // ensure GL_LINEAR filtering is used for scaling down to half the size - fboPainter.setRenderHint(QPainter::SmoothPixmapTransform); - fboPainter.setCompositionMode(QPainter::CompositionMode_Source); - fboPainter.drawPixmap(qMaxCachedBlurLevel / 2, qMaxCachedBlurLevel / 2, - targetRect.width() / 2 - qMaxCachedBlurLevel, targetRect.height() / 2 - qMaxCachedBlurLevel, src); - - GLuint textures[qNumCachedBlurTextures]; // blur textures - glGenTextures(qNumCachedBlurTextures, textures); - GLuint temp; // temp texture - glGenTextures(1, &temp); - - initializeTexture(temp, fbo->width(), fbo->height()); - m_textureSize = fbo->size(); - - int currentBlur = 0; - - QRect fboRect(0, 0, fbo->width(), fbo->height()); - GLuint sourceTexture = fbo->texture(); - for (int i = 0; i < qNumCachedBlurTextures; ++i) { - int targetBlur = qCachedBlurLevels[i] / 2; - - int blurDelta = qRound(qSqrt(targetBlur * targetBlur - currentBlur * currentBlur)); - QByteArray source = generateGaussianShader(blurDelta); - filter->setSource(source); - - currentBlur = targetBlur; - - // now we're going to be nasty and keep using the same FBO with different textures - glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, temp, 0); - - m_horizontalBlur = true; - filter->setOnPainter(&fboPainter); - engine->drawTexture(fboRect, sourceTexture, fbo->size(), fboRect); - filter->removeFromPainter(&fboPainter); - - sourceTexture = textures[i]; - initializeTexture(sourceTexture, fbo->width(), fbo->height()); - - glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, textures[i], 0); - - m_horizontalBlur = false; - filter->setOnPainter(&fboPainter); - engine->drawTexture(fboRect, temp, fbo->size(), fboRect); - filter->removeFromPainter(&fboPainter); - } - - glDeleteTextures(1, &temp); + uint mod = x % multiplier; + if (mod == 0) + return x; + return x + multiplier - mod; +} - // reattach the original FBO texture - glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, fbo->texture(), 0); +void qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHeight, int srcStride, + quint32 *dest, int dstStride); - fboPainter.end(); +bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const +{ + if (radius() < 1) { + painter->drawPixmap(pos, src); + return true; + } - qgl_fbo_pool()->release(fbo); + qreal actualRadius = radius(); - info = new QGLBlurTextureInfo(fboRect.size(), textures); - } + QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - if (!m_haveCached || !m_animatedBlur) { - m_haveCached = true; - m_animatedBlur = true; - m_hints = QGraphicsBlurEffect::AnimationHint; - filter->setSource(qt_gl_interpolate_filter); - } + QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx); + QGLBlurTextureInfo *info = 0; + int padding = nextMultiple(qCeil(actualRadius), qAnimatedBlurLevelIncrement); + QRect targetRect = src.rect().adjusted(-padding, -padding, padding, padding); - QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); - painter->setRenderHint(QPainter::SmoothPixmapTransform); - filter->setOnPainter(painter); + // pad so that we'll be able to half-scale qMaxBlurHalfScaleLevel times + targetRect.setWidth((targetRect.width() + (qMaxBlurHalfScaleLevel-1)) & ~(qMaxBlurHalfScaleLevel-1)); + targetRect.setHeight((targetRect.height() + (qMaxBlurHalfScaleLevel-1)) & ~(qMaxBlurHalfScaleLevel-1)); - qreal logRadius = qLn(radius()); + QSize textureSize; - int t; - for (t = -1; t < qNumCachedBlurTextures - 2; ++t) { - if (logRadius < qLogBlurLevel(t+1)) - break; - } + info = blurTextureCache->takeBlurTextureInfo(src); + if (!info || info->radius() < actualRadius) { + QSize paddedSize = targetRect.size() / 2; - qreal logBase = t >= 0 ? qLogBlurLevel(t) : 0; - m_t = qBound(qreal(0), (logRadius - logBase) / (qLogBlurLevel(t+1) - logBase), qreal(1)); + QImage padded(paddedSize.height(), paddedSize.width(), QImage::Format_ARGB32_Premultiplied); + padded.fill(0); - m_textureSize = info->size(); + if (info) { + int oldPadding = qRound(info->radius()); - glActiveTexture(GL_TEXTURE0 + 3); - if (t >= 0) { - glBindTexture(GL_TEXTURE_2D, info->textureId(t)); - m_targetSize = info->size(); + QPainter p(&padded); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.drawImage((padding - oldPadding) / 2, (padding - oldPadding) / 2, info->paddedImage()); + p.end(); } else { - QGLTexture *texture = - ctx->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, - QGLContext::InternalBindOption - | QGLContext::CanFlipNativePixmapBindOption); - m_targetSize = src.size(); - if (!(texture->options & QGLContext::InvertedYBindOption)) - m_targetSize.setHeight(-m_targetSize.height()); + // TODO: combine byteswapping and memrotating into one by declaring + // custom GL_RGBA pixel type and qt_colorConvert template for it + QImage prepadded = qt_halfScaled(src.toImage()).convertToFormat(QImage::Format_ARGB32_Premultiplied); + + // byte-swap and memrotates in one go + qt_memrotate90_gl(reinterpret_cast<const quint32*>(prepadded.bits()), + prepadded.width(), prepadded.height(), prepadded.bytesPerLine(), + reinterpret_cast<quint32*>(padded.scanLine(padding / 2)) + padding / 2, + padded.bytesPerLine()); } - // restrict the target rect to the max of the radii we are interpolating between - int radiusDelta = qMaxCachedBlurLevel - qCachedBlurLevels[t+1]; - targetRect = targetRect.translated(pos.toPoint()).adjusted(radiusDelta, radiusDelta, -radiusDelta, -radiusDelta); - - radiusDelta /= 2; - QRect sourceRect = QRect(QPoint(), m_textureSize).adjusted(radiusDelta, radiusDelta, -radiusDelta, -radiusDelta); - - engine->drawTexture(targetRect, info->textureId(t+1), m_textureSize, sourceRect); - - glActiveTexture(GL_TEXTURE0 + 3); - glBindTexture(GL_TEXTURE_2D, 0); - - filter->removeFromPainter(painter); - blurTextureCache->insertBlurTextureInfo(src, info); - - return true; - } + delete info; + info = new QGLBlurTextureInfo(padded, generateBlurTexture(paddedSize), padding); - if (blurTextureCache->hasBlurTextureInfo(src)) - blurTextureCache->clearBlurTextureInfo(src); - - int actualRadius = qRound(radius()); - int filterRadius = actualRadius; - int fastRadii[] = { 1, 2, 3, 5, 8, 15, 25 }; - if (!(m_hints & QGraphicsBlurEffect::QualityHint)) { - uint i = 0; - for (; i < (sizeof(fastRadii)/sizeof(*fastRadii))-1; ++i) { - if (fastRadii[i+1] > filterRadius) - break; - } - filterRadius = fastRadii[i]; + textureSize = paddedSize; + } else { + textureSize = QSize(info->paddedImage().height(), info->paddedImage().width()); } - m_singlePass = filterRadius <= 3; - - if (!m_haveCached || m_animatedBlur || filterRadius != m_cachedRadius) { - // Only regenerate the shader from source if parameters have changed. - m_haveCached = true; - m_animatedBlur = false; - m_cachedRadius = filterRadius; - QByteArray source = generateGaussianShader(filterRadius, m_singlePass); - filter->setSource(source); + actualRadius *= qreal(0.5); + int level = 1; + for (; level < qMaxBlurHalfScaleLevel; ++level) { + if (actualRadius <= 16) + break; + actualRadius *= qreal(0.5); } - QRect targetRect = QRectF(src.rect()).translated(pos).adjusted(-actualRadius, -actualRadius, actualRadius, actualRadius).toAlignedRect(); + const int s = (1 << level); - if (m_singlePass) { - // prepare for updateUniforms - m_textureSize = src.size(); + int prepadding = qRound(info->radius()); + padding = qMin(prepadding, qCeil(actualRadius) << level); + targetRect = src.rect().adjusted(-padding, -padding, padding, padding); - // ensure GL_LINEAR filtering is used - painter->setRenderHint(QPainter::SmoothPixmapTransform); - filter->setOnPainter(painter); - QBrush pixmapBrush = src; - pixmapBrush.setTransform(QTransform::fromTranslate(pos.x(), pos.y())); - painter->fillRect(targetRect, pixmapBrush); - filter->removeFromPainter(painter); - } else { - QGLFramebufferObjectFormat format; - format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB)); - QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(targetRect.size(), format); + targetRect.setWidth(targetRect.width() & ~(s-1)); + targetRect.setHeight(targetRect.height() & ~(s-1)); - if (!fbo) - return false; + int paddingDelta = (prepadding - padding) >> level; - // prepare for updateUniforms - m_textureSize = src.size(); + QRect subRect(paddingDelta, paddingDelta, targetRect.width() >> level, targetRect.height() >> level); + QImage sourceImage = info->paddedImage(level-1); - // horizontal pass, to pixmap - m_horizontalBlur = true; + QImage subImage(subRect.height(), subRect.width(), QImage::Format_ARGB32_Premultiplied); + qt_rectcopy((QRgb *)subImage.bits(), ((QRgb *)sourceImage.scanLine(paddingDelta)) + paddingDelta, + 0, 0, subRect.height(), subRect.width(), subImage.bytesPerLine(), sourceImage.bytesPerLine()); - QPainter fboPainter(fbo); + GLuint texture = info->texture(); - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); + qt_blurImage(subImage, actualRadius, blurHints() & QGraphicsBlurEffect::QualityHint, 1); - // ensure GL_LINEAR filtering is used - fboPainter.setRenderHint(QPainter::SmoothPixmapTransform); - fboPainter.setCompositionMode(QPainter::CompositionMode_Source); - filter->setOnPainter(&fboPainter); - QBrush pixmapBrush = src; - pixmapBrush.setTransform(QTransform::fromTranslate(actualRadius, actualRadius)); - fboPainter.fillRect(QRect(0, 0, targetRect.width(), targetRect.height()), pixmapBrush); - filter->removeFromPainter(&fboPainter); - fboPainter.end(); + // subtract one pixel off the end to prevent the bilinear sampling from sampling uninitialized data + QRect textureSubRect = subImage.rect().adjusted(0, 0, -1, -1); + QRectF targetRectF = QRectF(targetRect).adjusted(0, 0, -targetRect.width() / qreal(textureSize.width()), -targetRect.height() / qreal(textureSize.height())); - QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); + glBindTexture(GL_TEXTURE_2D, texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subImage.width(), subImage.height(), GL_RGBA, + GL_UNSIGNED_BYTE, const_cast<const QImage &>(subImage).bits()); - // vertical pass, to painter - m_horizontalBlur = false; - m_textureSize = fbo->size(); + QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); + painter->setRenderHint(QPainter::SmoothPixmapTransform); - painter->save(); - // ensure GL_LINEAR filtering is used - painter->setRenderHint(QPainter::SmoothPixmapTransform); - filter->setOnPainter(painter); - engine->drawTexture(targetRect, fbo->texture(), fbo->size(), QRect(QPoint(), targetRect.size()).translated(0, fbo->height() - targetRect.height())); - filter->removeFromPainter(painter); - painter->restore(); + // texture is flipped on the y-axis + targetRectF = QRectF(targetRectF.x(), targetRectF.bottom(), targetRectF.width(), -targetRectF.height()); + engine->drawTexture(targetRectF.translated(pos), texture, textureSize, textureSubRect); - qgl_fbo_pool()->release(fbo); - } + blurTextureCache->insertBlurTextureInfo(src, info); return true; } -void QGLPixmapBlurFilter::setUniforms(QGLShaderProgram *program) -{ - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - if (m_animatedBlur) { - program->setUniformValue("interpolateTarget", 3); - program->setUniformValue("interpolationValue", GLfloat(m_t)); - - if (m_textureSize == m_targetSize) { - program->setUniformValue("interpolateMapping", 0.0f, 0.0f, 1.0f, 1.0f); - } else { - float offsetX = (-qMaxCachedBlurLevel - 0.5) / qreal(m_targetSize.width()); - float offsetY = (-qMaxCachedBlurLevel - 0.5) / qreal(m_targetSize.height()); - - if (m_targetSize.height() < 0) - offsetY = 1 + offsetY; - - float scaleX = 2.0f * qreal(m_textureSize.width()) / qreal(m_targetSize.width()); - float scaleY = 2.0f * qreal(m_textureSize.height()) / qreal(m_targetSize.height()); - - program->setUniformValue("interpolateMapping", offsetX, offsetY, scaleX, scaleY); - } - - return; - } +static const char *qt_gl_drop_shadow_filter = + "uniform lowp vec4 shadowColor;" + "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords)" + "{" + " return shadowColor * texture2D(src, srcCoords.yx).a;" + "}"; - if (m_hints & QGraphicsBlurEffect::QualityHint) { - if (m_singlePass) - program->setUniformValue("delta", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height()); - else if (m_horizontalBlur) - program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0); - else - program->setUniformValue("delta", 0.0, 1.0 / m_textureSize.height()); - } else { - qreal blur = radius() / qreal(m_cachedRadius); - - if (m_singlePass) - program->setUniformValue("delta", blur / m_textureSize.width(), blur / m_textureSize.height()); - else if (m_horizontalBlur) - program->setUniformValue("delta", blur / m_textureSize.width(), 0.0); - else - program->setUniformValue("delta", 0.0, blur / m_textureSize.height()); - } -} -static inline qreal gaussian(qreal dx, qreal sigma) +QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter() { - return exp(-dx * dx / (2 * sigma * sigma)) / (Q_2PI * sigma * sigma); + setSource(qt_gl_drop_shadow_filter); } -QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool singlePass, bool dropShadow) +bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const { - Q_ASSERT(radius >= 1); - - radius = qMin(127, radius); - - static QCache<uint, QByteArray> shaderSourceCache; - uint key = radius | (int(singlePass) << 7) | (int(dropShadow) << 8); - QByteArray *cached = shaderSourceCache.object(key); - if (cached) - return *cached; - - QByteArray source; - source.reserve(1000); - source.append(qt_gl_texture_sampling_helper); - - source.append("uniform highp vec2 delta;\n"); - if (dropShadow) - source.append("uniform mediump vec4 shadowColor;\n"); - source.append("lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {\n"); - - QVector<qreal> sampleOffsets; - QVector<qreal> weights; - - QVector<qreal> gaussianComponents; - - qreal sigma = radius / 1.65; - - qreal sum = 0; - for (int i = -radius; i < radius; ++i) { - float value = gaussian(i, sigma); - gaussianComponents << value; - sum += value; - } - - // normalize - for (int i = 0; i < gaussianComponents.size(); ++i) - gaussianComponents[i] /= sum; - - for (int i = 0; i < gaussianComponents.size() - 1; i += 2) { - qreal weight = gaussianComponents.at(i) + gaussianComponents.at(i + 1); - qreal offset = i - radius + gaussianComponents.at(i + 1) / weight; - - sampleOffsets << offset; - weights << weight; - } - - int limit = sampleOffsets.size(); - if (singlePass) - limit *= limit; - - QByteArray baseCoordinate = "srcCoords"; - - for (int i = 0; i < limit; ++i) { - QByteArray coordinate = baseCoordinate; - - qreal weight; - if (singlePass) { - const int xIndex = i % sampleOffsets.size(); - const int yIndex = i / sampleOffsets.size(); - - const qreal deltaX = sampleOffsets.at(xIndex); - const qreal deltaY = sampleOffsets.at(yIndex); - weight = weights.at(xIndex) * weights.at(yIndex); - - if (!qFuzzyCompare(deltaX, deltaY)) { - coordinate.append(" + vec2(delta.x * float("); - coordinate.append(QByteArray::number(deltaX)); - coordinate.append("), delta.y * float("); - coordinate.append(QByteArray::number(deltaY)); - coordinate.append("))"); - } else if (!qFuzzyIsNull(deltaX)) { - coordinate.append(" + delta * float("); - coordinate.append(QByteArray::number(deltaX)); - coordinate.append(")"); - } - } else { - const qreal delta = sampleOffsets.at(i); - weight = weights.at(i); - if (!qFuzzyIsNull(delta)) { - coordinate.append(" + delta * float("); - coordinate.append(QByteArray::number(delta)); - coordinate.append(")"); - } - } - - if (i == 0) { - if (dropShadow) - source.append(" mediump float sample = "); - else - source.append(" mediump vec4 sample = "); - } else { - if (dropShadow) - source.append(" sample += "); - else - source.append(" sample += "); - } + QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this); - source.append("texture2D(src, "); - source.append(coordinate); - source.append(")"); + qreal r = blurRadius(); + QRectF targetRectUnaligned = QRectF(src.rect()).translated(pos + offset()).adjusted(-r, -r, r, r); + QRect targetRect = targetRectUnaligned.toAlignedRect(); - if (dropShadow) - source.append(".a"); + // ensure even dimensions (going to divide by two) + targetRect.setWidth((targetRect.width() + 1) & ~1); + targetRect.setHeight((targetRect.height() + 1) & ~1); - if (!qFuzzyCompare(weight, qreal(1))) { - source.append(" * float("); - source.append(QByteArray::number(weight)); - source.append(");\n"); - } else { - source.append(";\n"); - } - } + QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); + QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx); - source.append(" return "); - if (dropShadow) - source.append("shadowColor * "); - source.append("sample;\n"); - source.append("}\n"); + QGLBlurTextureInfo *info = blurTextureCache->takeBlurTextureInfo(src); + if (!info || info->radius() != r) { + QImage half = qt_halfScaled(src.toImage().alphaChannel()); - cached = new QByteArray(source); - shaderSourceCache.insert(key, cached); + qreal rx = r + targetRect.left() - targetRectUnaligned.left(); + qreal ry = r + targetRect.top() - targetRectUnaligned.top(); - return source; -} + QImage image = QImage(targetRect.size() / 2, QImage::Format_Indexed8); + image.setColorTable(half.colorTable()); + image.fill(0); + int dx = qRound(rx * qreal(0.5)); + int dy = qRound(ry * qreal(0.5)); + qt_rectcopy(image.bits(), half.bits(), dx, dy, + half.width(), half.height(), + image.bytesPerLine(), half.bytesPerLine()); -QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter(QGraphicsBlurEffect::BlurHints hints) - : m_haveCached(false) - , m_cachedRadius(0) - , m_hints(hints) -{ -} + qt_blurImage(image, r * qreal(0.5), false, 1); -bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const -{ - QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this); + GLuint texture = generateBlurTexture(image.size(), GL_ALPHA); - int actualRadius = qRound(blurRadius()); - int filterRadius = actualRadius; - m_singlePass = filterRadius <= 3; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width(), image.height(), GL_ALPHA, + GL_UNSIGNED_BYTE, image.bits()); - if (!m_haveCached || filterRadius != m_cachedRadius) { - // Only regenerate the shader from source if parameters have changed. - m_haveCached = true; - m_cachedRadius = filterRadius; - QByteArray source = QGLPixmapBlurFilter::generateGaussianShader(filterRadius, m_singlePass, true); - filter->setSource(source); + info = new QGLBlurTextureInfo(image, texture, r); } - QRect targetRect = QRectF(src.rect()).translated(pos + offset()).adjusted(-actualRadius, -actualRadius, actualRadius, actualRadius).toAlignedRect(); - - if (m_singlePass) { - // prepare for updateUniforms - m_textureSize = src.size(); - - painter->save(); - // ensure GL_LINEAR filtering is used - painter->setRenderHint(QPainter::SmoothPixmapTransform); - filter->setOnPainter(painter); - QBrush pixmapBrush = src; - pixmapBrush.setTransform(QTransform::fromTranslate(pos.x() + offset().x(), pos.y() + offset().y())); - painter->fillRect(targetRect, pixmapBrush); - filter->removeFromPainter(painter); - painter->restore(); - } else { - QGLFramebufferObjectFormat format; - format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB)); - QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(targetRect.size(), format); - - if (!fbo) - return false; - - // prepare for updateUniforms - m_textureSize = src.size(); - - // horizontal pass, to pixmap - m_horizontalBlur = true; - - QPainter fboPainter(fbo); + GLuint texture = info->texture(); - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); - - // ensure GL_LINEAR filtering is used - fboPainter.setRenderHint(QPainter::SmoothPixmapTransform); - fboPainter.setCompositionMode(QPainter::CompositionMode_Source); - filter->setOnPainter(&fboPainter); - QBrush pixmapBrush = src; - pixmapBrush.setTransform(QTransform::fromTranslate(actualRadius, actualRadius)); - fboPainter.fillRect(QRect(0, 0, targetRect.width(), targetRect.height()), pixmapBrush); - filter->removeFromPainter(&fboPainter); - fboPainter.end(); - - QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); + filter->setOnPainter(painter); - // vertical pass, to painter - m_horizontalBlur = false; - m_textureSize = fbo->size(); + QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); + painter->setRenderHint(QPainter::SmoothPixmapTransform); - painter->save(); - // ensure GL_LINEAR filtering is used - painter->setRenderHint(QPainter::SmoothPixmapTransform); - filter->setOnPainter(painter); - engine->drawTexture(targetRect, fbo->texture(), fbo->size(), QRectF(0, fbo->height() - targetRect.height(), targetRect.width(), targetRect.height())); - filter->removeFromPainter(painter); - painter->restore(); + engine->drawTexture(targetRect, texture, info->paddedImage().size(), info->paddedImage().rect()); - qgl_fbo_pool()->release(fbo); - } + filter->removeFromPainter(painter); // Now draw the actual pixmap over the top. painter->drawPixmap(pos, src, srcRect); + blurTextureCache->insertBlurTextureInfo(src, info); + return true; } void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program) { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - QColor col = color(); - if (m_horizontalBlur && !m_singlePass) { - program->setUniformValue("shadowColor", 1.0f, 1.0f, 1.0f, 1.0f); - } else { - qreal alpha = col.alphaF(); - program->setUniformValue("shadowColor", col.redF() * alpha, - col.greenF() * alpha, - col.blueF() * alpha, - alpha); - } - - if (m_hints & QGraphicsBlurEffect::QualityHint) { - if (m_singlePass) - program->setUniformValue("delta", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height()); - else if (m_horizontalBlur) - program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0); - else - program->setUniformValue("delta", 0.0, 1.0 / m_textureSize.height()); - } else { - qreal blur = blurRadius() / qreal(m_cachedRadius); - - if (m_singlePass) - program->setUniformValue("delta", blur / m_textureSize.width(), blur / m_textureSize.height()); - else if (m_horizontalBlur) - program->setUniformValue("delta", blur / m_textureSize.width(), 0.0); - else - program->setUniformValue("delta", 0.0, blur / m_textureSize.height()); - } + qreal alpha = col.alphaF(); + program->setUniformValue("shadowColor", col.redF() * alpha, + col.greenF() * alpha, + col.blueF() * alpha, + alpha); } QT_END_NAMESPACE diff --git a/src/openvg/openvg.pro b/src/openvg/openvg.pro index 8927c4c..c8c9917 100644 --- a/src/openvg/openvg.pro +++ b/src/openvg/openvg.pro @@ -16,11 +16,13 @@ HEADERS += \ qpaintengine_vg_p.h \ qpixmapdata_vg_p.h \ qpixmapfilter_vg_p.h \ - qvgcompositionhelper_p.h + qvgcompositionhelper_p.h \ + qvgimagepool_p.h SOURCES += \ qpaintengine_vg.cpp \ qpixmapdata_vg.cpp \ - qpixmapfilter_vg.cpp + qpixmapfilter_vg.cpp \ + qvgimagepool.cpp contains(QT_CONFIG, egl) { HEADERS += \ diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp index 6b829dd..04fee08 100644 --- a/src/openvg/qpaintengine_vg.cpp +++ b/src/openvg/qpaintengine_vg.cpp @@ -43,6 +43,7 @@ #include "qpixmapdata_vg_p.h" #include "qpixmapfilter_vg_p.h" #include "qvgcompositionhelper_p.h" +#include "qvgimagepool_p.h" #if !defined(QT_NO_EGL) #include <QtGui/private/qegl_p.h> #include "qwindowsurface_vgegl_p.h" @@ -1018,7 +1019,7 @@ static VGImage toVGImage const uchar *pixels = img.bits(); - VGImage vgImg = vgCreateImage + VGImage vgImg = QVGImagePool::instance()->createPermanentImage (format, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER); vgImageSubData (vgImg, pixels, img.bytesPerLine(), format, 0, 0, @@ -1063,7 +1064,7 @@ static VGImage toVGImageSubRect const uchar *pixels = img.bits() + bpp * sr.x() + img.bytesPerLine() * sr.y(); - VGImage vgImg = vgCreateImage + VGImage vgImg = QVGImagePool::instance()->createPermanentImage (format, sr.width(), sr.height(), VG_IMAGE_QUALITY_FASTER); vgImageSubData (vgImg, pixels, img.bytesPerLine(), format, 0, 0, @@ -1084,7 +1085,7 @@ static VGImage toVGImageWithOpacity(const QImage & image, qreal opacity) const uchar *pixels = img.bits(); - VGImage vgImg = vgCreateImage + VGImage vgImg = QVGImagePool::instance()->createPermanentImage (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER); vgImageSubData (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, @@ -1106,7 +1107,7 @@ static VGImage toVGImageWithOpacitySubRect const uchar *pixels = img.bits(); - VGImage vgImg = vgCreateImage + VGImage vgImg = QVGImagePool::instance()->createPermanentImage (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER); vgImageSubData (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, @@ -1194,6 +1195,12 @@ VGPaintType QVGPaintEnginePrivate::setBrush if (pd->classId() == QPixmapData::OpenVGClass) { QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); vgImg = vgpd->toVGImage(); + + // We don't want the pool to reclaim this image + // because we cannot predict when the paint object + // will stop using it. Replacing the image with + // new data will make the paint object invalid. + vgpd->detachImageFromPool(); } else { vgImg = toVGImage(*(pd->buffer())); deref = true; @@ -1201,6 +1208,7 @@ VGPaintType QVGPaintEnginePrivate::setBrush } else if (pd->classId() == QPixmapData::OpenVGClass) { QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); vgImg = vgpd->toVGImage(opacity); + vgpd->detachImageFromPool(); } else { vgImg = toVGImageWithOpacity(*(pd->buffer()), opacity); deref = true; diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp index af6f0f0..358ec4d 100644 --- a/src/openvg/qpixmapdata_vg.cpp +++ b/src/openvg/qpixmapdata_vg.cpp @@ -43,6 +43,7 @@ #include "qpaintengine_vg_p.h" #include <QtGui/private/qdrawhelper_p.h> #include "qvg_p.h" +#include "qvgimagepool_p.h" #ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE #include <graphics/sgimage.h> @@ -63,6 +64,8 @@ QVGPixmapData::QVGPixmapData(PixelType type) vgImageOpacity = VG_INVALID_HANDLE; cachedOpacity = 1.0f; recreate = true; + inImagePool = false; + inLRU = false; #if !defined(QT_NO_EGL) context = 0; qt_vg_register_pixmap(this); @@ -78,32 +81,43 @@ QVGPixmapData::~QVGPixmapData() #endif } +void QVGPixmapData::destroyImages() +{ + if (inImagePool) { + QVGImagePool *pool = QVGImagePool::instance(); + if (vgImage != VG_INVALID_HANDLE) + pool->releaseImage(this, vgImage); + if (vgImageOpacity != VG_INVALID_HANDLE) + pool->releaseImage(this, vgImageOpacity); + } else { + if (vgImage != VG_INVALID_HANDLE) + vgDestroyImage(vgImage); + if (vgImageOpacity != VG_INVALID_HANDLE) + vgDestroyImage(vgImageOpacity); + } + vgImage = VG_INVALID_HANDLE; + vgImageOpacity = VG_INVALID_HANDLE; + inImagePool = false; +} + void QVGPixmapData::destroyImageAndContext() { if (vgImage != VG_INVALID_HANDLE) { // We need to have a context current to destroy the image. #if !defined(QT_NO_EGL) if (context->isCurrent()) { - vgDestroyImage(vgImage); - if (vgImageOpacity != VG_INVALID_HANDLE) - vgDestroyImage(vgImageOpacity); + destroyImages(); } else { // We don't currently have a widget surface active, but we // need a surface to make the context current. So use the // shared pbuffer surface instead. context->makeCurrent(qt_vg_shared_surface()); - vgDestroyImage(vgImage); - if (vgImageOpacity != VG_INVALID_HANDLE) - vgDestroyImage(vgImageOpacity); + destroyImages(); context->lazyDoneCurrent(); } #else - vgDestroyImage(vgImage); - if (vgImageOpacity != VG_INVALID_HANDLE) - vgDestroyImage(vgImageOpacity); + destroyImages(); #endif - vgImage = VG_INVALID_HANDLE; - vgImageOpacity = VG_INVALID_HANDLE; } #if !defined(QT_NO_EGL) if (context) { @@ -234,26 +248,22 @@ VGImage QVGPixmapData::toVGImage() context = qt_vg_create_context(0, QInternal::Pixmap); #endif - if (recreate && prevSize != QSize(w, h)) { - if (vgImage != VG_INVALID_HANDLE) { - vgDestroyImage(vgImage); - vgImage = VG_INVALID_HANDLE; - } - if (vgImageOpacity != VG_INVALID_HANDLE) { - vgDestroyImage(vgImageOpacity); - vgImageOpacity = VG_INVALID_HANDLE; - } - } else if (recreate) { + if (recreate && prevSize != QSize(w, h)) + destroyImages(); + else if (recreate) cachedOpacity = -1.0f; // Force opacity image to be refreshed later. - } if (vgImage == VG_INVALID_HANDLE) { - vgImage = vgCreateImage - (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER); + vgImage = QVGImagePool::instance()->createImageForPixmap + (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER, this); // Bail out if we run out of GPU memory - try again next time. if (vgImage == VG_INVALID_HANDLE) return VG_INVALID_HANDLE; + + inImagePool = true; + } else if (inImagePool) { + QVGImagePool::instance()->useImage(this); } if (!source.isNull() && recreate) { @@ -282,8 +292,13 @@ VGImage QVGPixmapData::toVGImage(qreal opacity) // Create an alternative image for the selected opacity. if (vgImageOpacity == VG_INVALID_HANDLE || cachedOpacity != opacity) { if (vgImageOpacity == VG_INVALID_HANDLE) { - vgImageOpacity = vgCreateImage - (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER); + if (inImagePool) { + vgImageOpacity = QVGImagePool::instance()->createImageForPixmap + (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER, this); + } else { + vgImageOpacity = vgCreateImage + (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER); + } // Bail out if we run out of GPU memory - try again next time. if (vgImageOpacity == VG_INVALID_HANDLE) @@ -308,6 +323,14 @@ VGImage QVGPixmapData::toVGImage(qreal opacity) #endif } +void QVGPixmapData::detachImageFromPool() +{ + if (inImagePool) { + QVGImagePool::instance()->detachImage(this); + inImagePool = false; + } +} + void QVGPixmapData::hibernate() { // If the image was imported (e.g, from an SgImage under Symbian), @@ -319,6 +342,14 @@ void QVGPixmapData::hibernate() destroyImageAndContext(); } +void QVGPixmapData::reclaimImages() +{ + if (!inImagePool) + return; + forceToImage(); + destroyImages(); +} + extern int qt_defaultDpiX(); extern int qt_defaultDpiY(); @@ -411,14 +442,7 @@ void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) if (!context) context = qt_vg_create_context(0, QInternal::Pixmap); - if (vgImage != VG_INVALID_HANDLE) { - vgDestroyImage(vgImage); - vgImage = VG_INVALID_HANDLE; - } - if (vgImageOpacity != VG_INVALID_HANDLE) { - vgDestroyImage(vgImageOpacity); - vgImageOpacity = VG_INVALID_HANDLE; - } + destroyImages(); prevSize = QSize(); TInt err = 0; diff --git a/src/openvg/qpixmapdata_vg_p.h b/src/openvg/qpixmapdata_vg_p.h index c0bb098..4ff95c1 100644 --- a/src/openvg/qpixmapdata_vg_p.h +++ b/src/openvg/qpixmapdata_vg_p.h @@ -63,6 +63,7 @@ class RSGImage; QT_BEGIN_NAMESPACE class QEglContext; +class QVGImagePool; #if !defined(QT_NO_EGL) class QVGPixmapData; @@ -101,6 +102,9 @@ public: // Return the VGImage form for a specific opacity setting. virtual VGImage toVGImage(qreal opacity); + // Detach this image from the image pool. + virtual void detachImageFromPool(); + // Release the VG resources associated with this pixmap and copy // the pixmap's contents out of the GPU back into main memory. // The VG resource will be automatically recreated the next time @@ -109,6 +113,10 @@ public: // process via a SgImage). virtual void hibernate(); + // Called when the QVGImagePool wants to reclaim this pixmap's + // VGImage objects to reuse storage. + virtual void reclaimImages(); + QSize size() const { return QSize(w, h); } #if defined(Q_OS_SYMBIAN) @@ -123,8 +131,13 @@ protected: void cleanup(); #endif -#if !defined(QT_NO_EGL) private: + QVGPixmapData *nextLRU; + QVGPixmapData *prevLRU; + bool inLRU; + friend class QVGImagePool; + +#if !defined(QT_NO_EGL) QVGPixmapData *next; QVGPixmapData *prev; @@ -140,6 +153,7 @@ protected: qreal cachedOpacity; mutable QImage source; mutable bool recreate; + bool inImagePool; #if !defined(QT_NO_EGL) mutable QEglContext *context; #endif @@ -148,6 +162,7 @@ protected: QImage::Format sourceFormat() const; void destroyImageAndContext(); + void destroyImages(); }; QT_END_NAMESPACE diff --git a/src/openvg/qpixmapfilter_vg.cpp b/src/openvg/qpixmapfilter_vg.cpp index e17c728..aa526ed 100644 --- a/src/openvg/qpixmapfilter_vg.cpp +++ b/src/openvg/qpixmapfilter_vg.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qpixmapfilter_vg_p.h" +#include "qvgimagepool_p.h" #include <QtCore/qvarlengtharray.h> #include <QtGui/qpainter.h> @@ -82,9 +83,9 @@ void QVGPixmapConvolutionFilter::draw return; QSize size = pd->size(); - VGImage dstImage = vgCreateImage + VGImage dstImage = QVGImagePool::instance()->createTemporaryImage (VG_sARGB_8888_PRE, size.width(), size.height(), - VG_IMAGE_QUALITY_FASTER); + VG_IMAGE_QUALITY_FASTER, pd); if (dstImage == VG_INVALID_HANDLE) return; @@ -124,7 +125,7 @@ void QVGPixmapConvolutionFilter::draw if(child != dstImage) vgDestroyImage(child); - vgDestroyImage(dstImage); + QVGImagePool::instance()->releaseImage(0, dstImage); } QVGPixmapColorizeFilter::QVGPixmapColorizeFilter() @@ -155,9 +156,9 @@ void QVGPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const return; QSize size = pd->size(); - VGImage dstImage = vgCreateImage + VGImage dstImage = QVGImagePool::instance()->createTemporaryImage (VG_sARGB_8888_PRE, size.width(), size.height(), - VG_IMAGE_QUALITY_FASTER); + VG_IMAGE_QUALITY_FASTER, pd); if (dstImage == VG_INVALID_HANDLE) return; @@ -217,7 +218,7 @@ void QVGPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const if(child != dstImage) vgDestroyImage(child); - vgDestroyImage(dstImage); + QVGImagePool::instance()->releaseImage(0, dstImage); } QVGPixmapDropShadowFilter::QVGPixmapDropShadowFilter() @@ -248,9 +249,9 @@ void QVGPixmapDropShadowFilter::draw(QPainter *painter, const QPointF &dest, con return; QSize size = pd->size(); - VGImage dstImage = vgCreateImage + VGImage dstImage = QVGImagePool::instance()->createTemporaryImage (VG_A_8, size.width(), size.height(), - VG_IMAGE_QUALITY_FASTER); + VG_IMAGE_QUALITY_FASTER, pd); if (dstImage == VG_INVALID_HANDLE) return; @@ -282,7 +283,7 @@ void QVGPixmapDropShadowFilter::draw(QPainter *painter, const QPointF &dest, con if(child != dstImage) vgDestroyImage(child); - vgDestroyImage(dstImage); + QVGImagePool::instance()->releaseImage(0, dstImage); // Now draw the actual pixmap over the top. painter->drawPixmap(dest, src, srect); @@ -316,9 +317,9 @@ void QVGPixmapBlurFilter::draw(QPainter *painter, const QPointF &dest, const QPi return; QSize size = pd->size(); - VGImage dstImage = vgCreateImage + VGImage dstImage = QVGImagePool::instance()->createTemporaryImage (VG_sARGB_8888_PRE, size.width(), size.height(), - VG_IMAGE_QUALITY_FASTER); + VG_IMAGE_QUALITY_FASTER, pd); if (dstImage == VG_INVALID_HANDLE) return; @@ -347,7 +348,7 @@ void QVGPixmapBlurFilter::draw(QPainter *painter, const QPointF &dest, const QPi if(child != dstImage) vgDestroyImage(child); - vgDestroyImage(dstImage); + QVGImagePool::instance()->releaseImage(0, dstImage); } #endif diff --git a/src/openvg/qvgimagepool.cpp b/src/openvg/qvgimagepool.cpp new file mode 100644 index 0000000..93e6e03 --- /dev/null +++ b/src/openvg/qvgimagepool.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvgimagepool_p.h" +#include "qpixmapdata_vg_p.h" + +QT_BEGIN_NAMESPACE + +static QVGImagePool *qt_vg_image_pool = 0; + +class QVGImagePoolPrivate +{ +public: + QVGImagePoolPrivate() : lruFirst(0), lruLast(0) {} + + QVGPixmapData *lruFirst; + QVGPixmapData *lruLast; +}; + +QVGImagePool::QVGImagePool() + : d_ptr(new QVGImagePoolPrivate()) +{ +} + +QVGImagePool::~QVGImagePool() +{ +} + +QVGImagePool *QVGImagePool::instance() +{ + if (!qt_vg_image_pool) + qt_vg_image_pool = new QVGImagePool(); + return qt_vg_image_pool; +} + +void QVGImagePool::setImagePool(QVGImagePool *pool) +{ + if (qt_vg_image_pool != pool) + delete qt_vg_image_pool; + qt_vg_image_pool = pool; +} + +VGImage QVGImagePool::createTemporaryImage(VGImageFormat format, + VGint width, VGint height, + VGbitfield allowedQuality, + QVGPixmapData *keepData) +{ + VGImage image; + do { + image = vgCreateImage(format, width, height, allowedQuality); + if (image != VG_INVALID_HANDLE) + return image; + } while (reclaimSpace(format, width, height, keepData)); + qWarning("QVGImagePool: cannot reclaim sufficient space for a %dx%d temporary image", + width, height); + return VG_INVALID_HANDLE; +} + +VGImage QVGImagePool::createImageForPixmap(VGImageFormat format, + VGint width, VGint height, + VGbitfield allowedQuality, + QVGPixmapData *data) +{ + VGImage image; + do { + image = vgCreateImage(format, width, height, allowedQuality); + if (image != VG_INVALID_HANDLE) { + if (data) + moveToHeadOfLRU(data); + return image; + } + } while (reclaimSpace(format, width, height, data)); + qWarning("QVGImagePool: cannot reclaim sufficient space for a %dx%d pixmap", + width, height); + return VG_INVALID_HANDLE; +} + +VGImage QVGImagePool::createPermanentImage(VGImageFormat format, + VGint width, VGint height, + VGbitfield allowedQuality) +{ + VGImage image; + do { + image = vgCreateImage(format, width, height, allowedQuality); + if (image != VG_INVALID_HANDLE) + return image; + } while (reclaimSpace(format, width, height, 0)); + qWarning("QVGImagePool: cannot reclaim sufficient space for a %dx%d image", + width, height); + return VG_INVALID_HANDLE; +} + +void QVGImagePool::releaseImage(QVGPixmapData *data, VGImage image) +{ + // Very simple strategy at the moment: just destroy the image. + if (data) + removeFromLRU(data); + vgDestroyImage(image); +} + +void QVGImagePool::useImage(QVGPixmapData *data) +{ + moveToHeadOfLRU(data); +} + +void QVGImagePool::detachImage(QVGPixmapData *data) +{ + removeFromLRU(data); +} + +bool QVGImagePool::reclaimSpace(VGImageFormat format, + VGint width, VGint height, + QVGPixmapData *data) +{ + Q_UNUSED(format); // For future use in picking the best image to eject. + Q_UNUSED(width); + Q_UNUSED(height); + + if (data) + moveToHeadOfLRU(data); + + QVGPixmapData *lrudata = pixmapLRU(); + if (lrudata && lrudata != data) { + lrudata->reclaimImages(); + return true; + } + + return false; +} + +void QVGImagePool::hibernate() +{ + // Nothing to do here at the moment since the pool does not + // retain VGImage's after they have been released. +} + +void QVGImagePool::moveToHeadOfLRU(QVGPixmapData *data) +{ + Q_D(QVGImagePool); + if (data->inLRU) { + if (!data->prevLRU) + return; // Already at the head of the list. + removeFromLRU(data); + } + data->inLRU = true; + data->nextLRU = d->lruFirst; + data->prevLRU = 0; + if (d->lruFirst) + d->lruFirst->prevLRU = data; + else + d->lruLast = data; + d->lruFirst = data; +} + +void QVGImagePool::removeFromLRU(QVGPixmapData *data) +{ + Q_D(QVGImagePool); + if (!data->inLRU) + return; + if (data->nextLRU) + data->nextLRU->prevLRU = data->prevLRU; + else + d->lruLast = data->prevLRU; + if (data->prevLRU) + data->prevLRU->nextLRU = data->nextLRU; + else + d->lruFirst = data->nextLRU; + data->inLRU = false; +} + +QVGPixmapData *QVGImagePool::pixmapLRU() +{ + Q_D(QVGImagePool); + return d->lruLast; +} + +QT_END_NAMESPACE diff --git a/src/openvg/qvgimagepool_p.h b/src/openvg/qvgimagepool_p.h new file mode 100644 index 0000000..66a4998 --- /dev/null +++ b/src/openvg/qvgimagepool_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVGIMAGEPOOL_P_H +#define QVGIMAGEPOOL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qvg.h" +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_NAMESPACE + +class QVGPixmapData; +class QVGImagePoolPrivate; + +class Q_OPENVG_EXPORT QVGImagePool +{ +public: + QVGImagePool(); + virtual ~QVGImagePool(); + + static QVGImagePool *instance(); + + // This function can be used from system-specific graphics system + // plugins to alter the image allocation strategy. + static void setImagePool(QVGImagePool *pool); + + // Create a new VGImage from the pool with the specified parameters + // that is not associated with a pixmap. The VGImage is returned to + // the pool when releaseImage() is called. + // + // This function will call reclaimSpace() when vgCreateImage() fails. + // + // This function is typically called when allocating temporary + // VGImage's for pixmap filters. The "keepData" object will not + // be reclaimed if reclaimSpace() needs to be called. + virtual VGImage createTemporaryImage(VGImageFormat format, + VGint width, VGint height, + VGbitfield allowedQuality, + QVGPixmapData *keepData = 0); + + // Create a new VGImage with the specified parameters and associate + // it with "data". The QVGPixmapData will be notified when the + // VGImage needs to be reclaimed by the pool. + // + // This function will call reclaimSpace() when vgCreateImage() fails. + virtual VGImage createImageForPixmap(VGImageFormat format, + VGint width, VGint height, + VGbitfield allowedQuality, + QVGPixmapData *data); + + // Create a permanent VGImage with the specified parameters. + // If there is insufficient space for the vgCreateImage call, + // then this function will call reclaimSpace() and try again. + // + // The caller is responsible for calling vgDestroyImage() + // when it no longer needs the VGImage, as the image is not + // recorded in the image pool. + // + // This function is typically used for pattern brushes where + // the OpenVG engine is responsible for managing the lifetime + // of the VGImage, destroying it automatically when the brush + // is no longer in use. + virtual VGImage createPermanentImage(VGImageFormat format, + VGint width, VGint height, + VGbitfield allowedQuality); + + // Release a VGImage that is no longer required. + virtual void releaseImage(QVGPixmapData *data, VGImage image); + + // Notify the pool that a QVGPixmapData object is using + // an image again. This allows the pool to move the image + // within a least-recently-used list of QVGPixmapData objects. + virtual void useImage(QVGPixmapData *data); + + // Notify the pool that the VGImage's associated with a + // QVGPixmapData are being detached from the pool. The caller + // will become responsible for calling vgDestroyImage(). + virtual void detachImage(QVGPixmapData *data); + + // Reclaim space for an image allocation with the specified parameters. + // Returns true if space was reclaimed, or false if there is no + // further space that can be reclaimed. The "data" parameter + // indicates the pixmap that is trying to obtain space which should + // not itself be reclaimed. + virtual bool reclaimSpace(VGImageFormat format, + VGint width, VGint height, + QVGPixmapData *data); + + // Hibernate the image pool because the context is about to be + // destroyed. All VGImage's left in the pool should be released. + virtual void hibernate(); + +protected: + // Helper functions for managing the LRU list of QVGPixmapData objects. + void moveToHeadOfLRU(QVGPixmapData *data); + void removeFromLRU(QVGPixmapData *data); + QVGPixmapData *pixmapLRU(); + +private: + QScopedPointer<QVGImagePoolPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QVGImagePool) + Q_DISABLE_COPY(QVGImagePool) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/openvg/qwindowsurface_vgegl.cpp b/src/openvg/qwindowsurface_vgegl.cpp index 1571083..bda6096 100644 --- a/src/openvg/qwindowsurface_vgegl.cpp +++ b/src/openvg/qwindowsurface_vgegl.cpp @@ -42,6 +42,7 @@ #include "qwindowsurface_vgegl_p.h" #include "qpaintengine_vg_p.h" #include "qpixmapdata_vg_p.h" +#include "qvgimagepool_p.h" #include "qvg_p.h" #if !defined(QT_NO_EGL) @@ -360,6 +361,9 @@ void qt_vg_hibernate_pixmaps(QVGSharedContext *shared) pd = pd->next; } + // Hibernate any remaining VGImage's in the image pool. + QVGImagePool::instance()->hibernate(); + // Don't need the current context any more. shared->context->lazyDoneCurrent(); diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 8fcc0df..1a75cf6 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -725,16 +725,6 @@ void Generator::generateMetacall() needEditable |= p.editable.endsWith(')'); needUser |= p.user.endsWith(')'); } - bool needAnything = needGet - | needSet - | needReset - | needDesignable - | needScriptable - | needStored - | needEditable - | needUser; - if (!needAnything) - goto skip_properties; fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); if (needElse) @@ -904,7 +894,6 @@ void Generator::generateMetacall() fprintf(out, "\n#endif // QT_NO_PROPERTIES"); } - skip_properties: if (methodList.size() || cdef->signalList.size() || cdef->propertyList.size()) fprintf(out, "\n "); fprintf(out,"return _id;\n}\n"); |