src/hbcore/ovgeffects/hbvgeffect.cpp
changeset 0 16d8024aca5e
child 5 627c4a0fd0e7
equal deleted inserted replaced
-1:000000000000 0:16d8024aca5e
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (developer.feedback@nokia.com)
       
     6 **
       
     7 ** This file is part of the HbCore module of the UI Extensions for Mobile.
       
     8 **
       
     9 ** GNU Lesser General Public License Usage
       
    10 ** This file may be used under the terms of the GNU Lesser General Public
       
    11 ** License version 2.1 as published by the Free Software Foundation and
       
    12 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
       
    13 ** Please review the following information to ensure the GNU Lesser General
       
    14 ** Public License version 2.1 requirements will be met:
       
    15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    16 **
       
    17 ** In addition, as a special exception, Nokia gives you certain additional
       
    18 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    20 **
       
    21 ** If you have questions regarding the use of this file, please contact
       
    22 ** Nokia at developer.feedback@nokia.com.
       
    23 **
       
    24 ****************************************************************************/
       
    25 
       
    26 #include "hbvgeffect_p.h"
       
    27 #include "hbvgeffect_p_p.h"
       
    28 #include <QPainter>
       
    29 #include <QPaintEngine>
       
    30 #include <QPixmapCache>
       
    31 #include <QGraphicsScene>
       
    32 #include <QGraphicsItem>
       
    33 #include <QSet>
       
    34 #include <hbmainwindow.h>
       
    35 #include <hbmainwindow_p.h>
       
    36 
       
    37 /*!
       
    38  * \class HbVgEffect
       
    39  *
       
    40  * \brief Abstract base class for OpenVG effects.
       
    41  *
       
    42  * \internal
       
    43  * 
       
    44  * Brief guide for creating new effects:
       
    45  * <ul>
       
    46  *
       
    47  *    <li>Derive from HbVgEffect.
       
    48  *
       
    49  *    <li>Add getter/setter/property/signal for new effect parameters.
       
    50  *
       
    51  *    <li>In the setter make sure to call either updateEffectBoundingRect() or
       
    52  *    updateEffect(), depending on the parameter (some parameters affect the
       
    53  *    bounding rectangle, some not).
       
    54  *
       
    55  *    <li>Reimplement boundingRectFor().
       
    56  *
       
    57  *    <li>Implement performEffect(). This function will typically generate
       
    58  *    VGImages containing the filtered source image (or just the extra visuals
       
    59  *    that appear due to the effect) and draw them via the given painter's
       
    60  *    drawPixmap() (or vgDrawImage). Do not forget to draw the source image too,
       
    61  *    if needed.  The VGImage argument is passed in a variant to keep things
       
    62  *    building even without OpenVG headers/libraries.  Always use an appropriate
       
    63  *    ifdef for code that uses OpenVG types or functions.
       
    64  *
       
    65  * </ul>
       
    66  *
       
    67  * Note that these effects must not be installed using QGraphicsItem::setGraphicsEffect(),
       
    68  * use install() instead.
       
    69  *
       
    70  * \sa install()
       
    71  */
       
    72 
       
    73 /*!
       
    74  * \fn void HbVgEffect::performEffect(QPainter *painter, const QPointF &offset,
       
    75  * const QVariant &vgImage, const QSize &vgImageSize)
       
    76  *
       
    77  * This function must be implemented in derived classes, providing the actual
       
    78  * implementation for the effect generation.
       
    79  *
       
    80  * \a painter is the painter to be used (it is guaranteed to be an OpenVG-based
       
    81  * one), \a offset is the target position, the variant \a vgImage contains a
       
    82  * VGImage handle for the source image, and \a vgImageSize is the size of that
       
    83  * image. The handle in \a vgImage is actually the underlying VGImage of the
       
    84  * pixmap stored in d->srcPixmap.
       
    85  *
       
    86  * Note that the size of this pixmap (and thus vgImageSize) already includes the
       
    87  * extra margins added by the effect's boundingRectFor, so drawing the entire
       
    88  * pixmap at any other position than \a offset will lead to painting outside the
       
    89  * item's effective bounding rectangle which may cause artifacts on the screen.
       
    90  *
       
    91  * \sa QGraphicsEffect::boundingRectFor()
       
    92  */
       
    93 
       
    94 Q_GLOBAL_STATIC(QSet<QString>, cacheKeys)
       
    95 
       
    96 HbVgEffectPrivate::HbVgEffectPrivate()
       
    97     : q_ptr(0),
       
    98       paramsChanged(true),
       
    99       cacheInvalidated(true),
       
   100       opacity(1),
       
   101       caching(false),
       
   102       rootEffect(0),
       
   103       sourceGraphicsItem(0),
       
   104       mainWindow(0),
       
   105       lastUsedRotation(0),
       
   106       lastRotationTransformAngle(0)
       
   107 {
       
   108 }
       
   109 
       
   110 /*!
       
   111  * \internal
       
   112  */
       
   113 HbVgEffectPrivate::~HbVgEffectPrivate()
       
   114 {
       
   115 }
       
   116 
       
   117 #ifdef HB_EFFECTS_OPENVG
       
   118 /*!
       
   119  * Makes sure that the given pixmap has the given \a size.  Returns
       
   120  * the underlying VGImage handle.  Typically used with dstPixmap and
       
   121  * tmpPixmap.
       
   122  *
       
   123  * \internal
       
   124  */
       
   125 VGImage HbVgEffectPrivate::ensurePixmap(QPixmap *pixmap, const QSize& size)
       
   126 {
       
   127     if (pixmap->size() != size)
       
   128         *pixmap = QPixmap(size);
       
   129     return qPixmapToVGImage(*pixmap);
       
   130 }
       
   131 #endif
       
   132 
       
   133 /*!
       
   134  * Invalidates the cache but only if it has not been done already.
       
   135  *
       
   136  * \internal
       
   137  */
       
   138 void HbVgEffectPrivate::ensureCacheInvalidated()
       
   139 {
       
   140     if (!cacheInvalidated) {
       
   141 #ifdef HBVG_TRACES
       
   142         Q_Q(HbVgEffect);
       
   143         qDebug("HbVgEffect [%x]: invalidating cache", (int) q);
       
   144 #endif
       
   145         cacheInvalidated = true;
       
   146         notifyCacheInvalidated();
       
   147     }
       
   148 }
       
   149 
       
   150 /*!
       
   151  * Returns the rotation of the graphics view in degrees.
       
   152  *
       
   153  * For some effects it is important to know if the graphics view has been
       
   154  * transformed or not (e.g. a reflection effect may need to draw at a different
       
   155  * position when the graphics view is rotated).
       
   156  *
       
   157  * \internal
       
   158  */
       
   159 qreal HbVgEffectPrivate::mainWindowRotation() const
       
   160 {
       
   161     if (!mainWindow) {
       
   162         Q_Q(const HbVgEffect);
       
   163         const QGraphicsItem *srcItem = q->sourceItemForRoot();
       
   164         if (srcItem) {
       
   165             QGraphicsScene *scene = srcItem->scene();
       
   166             if (scene) {
       
   167                 QList<QGraphicsView *> views = scene->views();
       
   168                 foreach (QGraphicsView *view, views) {
       
   169                     mainWindow = qobject_cast<HbMainWindow *>(view);
       
   170                     if (mainWindow)
       
   171                         break;
       
   172                 }
       
   173             }
       
   174         }
       
   175     }
       
   176     return mainWindow ? HbMainWindowPrivate::d_ptr(mainWindow)->rotation() : 0;
       
   177 }
       
   178 
       
   179 /*!
       
   180  * Returns a transformation that defines the graphics view's current rotation.
       
   181  *
       
   182  * \internal
       
   183  */
       
   184 QTransform HbVgEffectPrivate::rotationTransform() const
       
   185 {
       
   186     int rotation = (int) mainWindowRotation();
       
   187     if (rotation != lastRotationTransformAngle) {
       
   188         lastRotationTransformAngle = rotation;
       
   189         lastRotationTransform = QTransform().rotate(rotation);
       
   190     }
       
   191     return lastRotationTransform;
       
   192 }
       
   193     
       
   194 /*!
       
   195  * Maps the given translation offset to an offset that is based on the current
       
   196  * rotation of the graphics view. Effects are drawing directly, in device
       
   197  * coordinates, however they must take into account the rotation of the graphics
       
   198  * view too. Therefore any translation they use must be mapped using this
       
   199  * function. (NB. this does not apply to the offset argument of
       
   200  * performEffect()).
       
   201  *
       
   202  * For example if a dropshadow effect gets (x, y) as the offset of the shadow,
       
   203  * it must call this function instead of using the offset as it is, because the
       
   204  * correct device coordinates for the offset may be something different,
       
   205  * e.g. (y, -x) if the graphics view is rotated -90 degrees.
       
   206  *
       
   207  * \internal
       
   208  */
       
   209 QPointF HbVgEffectPrivate::mapOffset(const QPointF &offset) const
       
   210 {
       
   211     return rotationTransform().map(offset);
       
   212 }
       
   213 
       
   214 /*!
       
   215  * Returns a size that has the width and height swapped if the current rotation
       
   216  * of the graphics view implies that. Typically used with size-like values,
       
   217  * e.g. the radius for a blur effect.
       
   218  *
       
   219  * \internal
       
   220  */
       
   221 QSizeF HbVgEffectPrivate::mapSize(const QSizeF &size) const
       
   222 {
       
   223     QPointF p = mapOffset(QPointF(size.width(), size.height()));
       
   224     return QSizeF(qAbs(p.x()), qAbs(p.y()));
       
   225 }
       
   226 
       
   227 /*!
       
   228  * \internal
       
   229  *
       
   230  * Returns a cache key that is unique to this effect instance (but only for the
       
   231  * lifetime of the instance).
       
   232  */
       
   233 inline QString cacheKey(const HbVgEffect *e)
       
   234 {
       
   235     return QString::number((qptrdiff) e);
       
   236 }
       
   237 
       
   238 HbVgEffect::HbVgEffect(QObject *parent)
       
   239     : QGraphicsEffect(parent), d_ptr(new HbVgEffectPrivate)
       
   240 {
       
   241     Q_D(HbVgEffect);
       
   242     d->q_ptr = this;
       
   243 }
       
   244 
       
   245 HbVgEffect::HbVgEffect(HbVgEffectPrivate &dd, QObject *parent)
       
   246     : QGraphicsEffect(parent), d_ptr(&dd)
       
   247 {
       
   248     Q_D(HbVgEffect);
       
   249     d->q_ptr = this;
       
   250 }
       
   251 
       
   252 HbVgEffect::~HbVgEffect()
       
   253 {
       
   254     // Get rid of the cached pixmap if it is still present.
       
   255     QString key = cacheKey(this);
       
   256     QPixmapCache::remove(key);
       
   257     cacheKeys()->remove(key);
       
   258     delete d_ptr;
       
   259 }
       
   260 
       
   261 /*!
       
   262  * Updates the source item (and thus the effect). Also sets the paramsChanged
       
   263  * and cacheInvalidated flags. This function should be called from the effect
       
   264  * parameter setters only.
       
   265  *
       
   266  * Note that this is not sufficient if the results of boundingRectFor() are also
       
   267  * changing due to an updated parameter value. In that case call
       
   268  * updateEffectBoundingRect() instead.
       
   269  *
       
   270  * \sa updateEffectBoundingRect()
       
   271  */
       
   272 void HbVgEffect::updateEffect()
       
   273 {
       
   274     Q_D(HbVgEffect);
       
   275     d->paramsChanged = true;
       
   276     d->ensureCacheInvalidated();
       
   277     if (d->rootEffect) {
       
   278         d->rootEffect->updateEffect();
       
   279     } else {
       
   280         update();
       
   281     }
       
   282 }
       
   283 
       
   284 /*!
       
   285  * Notifies that the bounding rectangle for the effect (i.e. the return value of
       
   286  * boundingRectFor()) is possibly different than before. Also updates the source
       
   287  * item (and thus the effect) and sets the paramsChanged and cacheInvalidated
       
   288  * flags. This function should be called from the effect parameter setters only.
       
   289  *
       
   290  * When changing effect parameters that have no effect on the bounding
       
   291  * rectangle, use updateEffect() instead because that might be more efficient.
       
   292  *
       
   293  * \sa updateEffect()
       
   294  */
       
   295 void HbVgEffect::updateEffectBoundingRect()
       
   296 {
       
   297     Q_D(HbVgEffect);
       
   298     d->paramsChanged = true;
       
   299     d->ensureCacheInvalidated();
       
   300     if (d->rootEffect) {
       
   301         d->rootEffect->updateBoundingRect();
       
   302     } else {
       
   303         updateBoundingRect();
       
   304     }
       
   305 }
       
   306 
       
   307 /*!
       
   308  * Enables delegation of update requests to the given \a effect.
       
   309  *
       
   310  * This is needed when there are several effects in a chain and thus all update
       
   311  * requests should be routed from the individual elements to the container
       
   312  * effect.
       
   313  */
       
   314 void HbVgEffect::setChainRoot(HbVgEffect *effect)
       
   315 {
       
   316     Q_D(HbVgEffect);
       
   317     d->rootEffect = effect;
       
   318 }
       
   319 
       
   320 /*!
       
   321  * Returns the current root of the chain or null if this effect is not part of
       
   322  * any delegation chain. Do not assume that the returned effect is a
       
   323  * HbVgChainedEffect, it may also be something else (in theory), so use
       
   324  * qobject_cast if the return value needs to be casted to a more specific type.
       
   325  */
       
   326 HbVgEffect *HbVgEffect::chainRoot() const
       
   327 {
       
   328     Q_D(const HbVgEffect);
       
   329     return d->rootEffect;
       
   330 }
       
   331 
       
   332 /*!
       
   333  * Returns the current opacity parameter of the effect.
       
   334  *
       
   335  * \sa setOpacity()
       
   336  */
       
   337 qreal HbVgEffect::opacity() const
       
   338 {
       
   339     Q_D(const HbVgEffect);
       
   340     return d->opacity;
       
   341 }
       
   342 
       
   343 /*!
       
   344  * Sets the \a opacity parameter of the effect.
       
   345  * The value must fall between 0.0 and 1.0.
       
   346  *
       
   347  * \sa opacity()
       
   348  */
       
   349 void HbVgEffect::setOpacity(qreal opacity)
       
   350 {
       
   351     Q_D(HbVgEffect);
       
   352     if (d->opacity == opacity)
       
   353         return;
       
   354     d->opacity = opacity;
       
   355     updateEffect();
       
   356     emit opacityChanged(opacity);
       
   357 }
       
   358 
       
   359 /*!
       
   360  * \reimp
       
   361  *
       
   362  * Implementation of QGraphicsEffect's draw(). After setting up the environment
       
   363  * for direct OpenVG painting, the work is delegated to the performEffect()
       
   364  * implementation of the derived classes.
       
   365  */
       
   366 void HbVgEffect::draw(QPainter *painter)
       
   367 {
       
   368     // Just draw the source without effects if the painter's paint engine
       
   369     // is not using OpenVG.
       
   370     if (painter->paintEngine()->type() != QPaintEngine::OpenVG) {
       
   371         drawSource(painter);
       
   372         return;
       
   373     }
       
   374 
       
   375 #ifdef HB_EFFECTS_OPENVG
       
   376     Q_D(HbVgEffect);
       
   377     // Render the source into a pixmap. Note that the effect's modified bounding
       
   378     // rectangle is already taken into account in the size of the pixmap created
       
   379     // here, so there is no need to waste time with boundingRectFor() calls
       
   380     // later in performEffect() when creating temporary buffers.
       
   381     QPoint offset;
       
   382     // Don't bother with optimizations based on sourceIsPixmap() because we will
       
   383     // rarely be used on QGraphicsPixmapItems anyway.
       
   384     d->srcPixmap = sourcePixmap(Qt::DeviceCoordinates, &offset);
       
   385 
       
   386     // Pull out the underlying VGImage from the pixmap.
       
   387     VGImage vgImage = qPixmapToVGImage(d->srcPixmap);
       
   388     // If something went wrong then just draw the source without effects.
       
   389     if (vgImage == VG_INVALID_HANDLE) {
       
   390         drawSource(painter);
       
   391         return;
       
   392     }
       
   393 
       
   394     // Save the world transformation and then reset it.
       
   395     d->worldTransform = painter->worldTransform();
       
   396     painter->setWorldTransform(QTransform());
       
   397 
       
   398     // Invalidate the cache if the rotation of the graphics view has changed.
       
   399     qreal rotation = d->mainWindowRotation();
       
   400     if (rotation != d->lastUsedRotation) {
       
   401         d->lastUsedRotation = rotation;
       
   402         d->ensureCacheInvalidated();
       
   403     }
       
   404 
       
   405     // Enter raw VG mode.
       
   406     painter->beginNativePainting();
       
   407     // Draw something.
       
   408     performEffect(painter, offset, QVariant::fromValue<VGImage>(vgImage), d->srcPixmap.size());
       
   409     // Clear the param/cache flags as all data should be up-to-date now.
       
   410     d->paramsChanged = d->cacheInvalidated = false;
       
   411     // Leave raw VG mode.
       
   412     painter->endNativePainting();
       
   413 
       
   414     // Restore the painter's previously set transformations.
       
   415     painter->setWorldTransform(d->worldTransform);
       
   416 
       
   417 #else
       
   418     // OpenVG code disabled => effect is not shown (but have the source drawn still).
       
   419     drawSource(painter);
       
   420 #endif
       
   421 }
       
   422 
       
   423 /*!
       
   424  *  \reimp
       
   425  */
       
   426 void HbVgEffect::sourceChanged(ChangeFlags flags)
       
   427 {
       
   428     // If the bounding rectangle or appearance of the source has changed then
       
   429     // set our flag to indicate that any cached visual data will have to be
       
   430     // dropped.
       
   431     if (flags & SourceBoundingRectChanged || flags & SourceInvalidated) {
       
   432         Q_D(HbVgEffect);
       
   433         d->ensureCacheInvalidated();
       
   434     }
       
   435 }
       
   436 
       
   437 /*!
       
   438  * Returns the current status of caching.
       
   439  *
       
   440  * \sa setCaching()
       
   441  */
       
   442 bool HbVgEffect::caching() const
       
   443 {
       
   444     Q_D(const HbVgEffect);
       
   445     return d->caching;
       
   446 }
       
   447 
       
   448 /*!
       
   449  * Enables or disables caching of the output of the effects.
       
   450  *
       
   451  * By default caching is disabled.
       
   452  *
       
   453  * Note that caching may not cause any increase in performance if the size or
       
   454  * apperance of the source item is changing rapidly, it is only useful for items
       
   455  * that are static enough.
       
   456  *
       
   457  * Enabling this setting may have no effect because some subclasses may not be
       
   458  * able to support it, it is treated as a hint only.
       
   459  *
       
   460  * \sa caching()
       
   461  */
       
   462 void HbVgEffect::setCaching(bool caching)
       
   463 {
       
   464     Q_D(HbVgEffect);
       
   465     if (d->caching == caching)
       
   466         return;
       
   467     d->caching = caching;
       
   468     emit cachingChanged(caching);
       
   469 }
       
   470 
       
   471 /*!
       
   472  * Returns the cached pixmap that is associated with this effect instance. Will
       
   473  * return a null pixmap if caching is disabled or there was no tryCache() call
       
   474  * for this instance before or the cache has been invalidated (e.g. because the
       
   475  * source item's appearance has changed).
       
   476  *
       
   477  * \sa tryCache()
       
   478  */
       
   479 QPixmap HbVgEffect::cached(const QSize &size) const
       
   480 {
       
   481     Q_D(const HbVgEffect);
       
   482     if (d->caching) {
       
   483         QString key = cacheKey(this);
       
   484         if (d->cacheInvalidated) {
       
   485             QPixmapCache::remove(key);
       
   486             cacheKeys()->remove(key);
       
   487         } else {
       
   488             QPixmap cachedPm;
       
   489             // Check if we have a pixmap in the cache, then check the size
       
   490             // too. The size check is important because the effects will
       
   491             // typically render their source using device coordinates which
       
   492             // causes a clipping to the device viewport, therefore the cached
       
   493             // pixmap for an item that was/is clipped should not be used.
       
   494             if (QPixmapCache::find(key, &cachedPm)
       
   495                 && (size.isNull() || cachedPm.size() == size))
       
   496             {
       
   497 #ifdef HBVG_TRACES
       
   498                 qDebug("HbVgEffect [%x]: cache hit", (int) this);
       
   499 #endif
       
   500                 return cachedPm;
       
   501             }
       
   502         }
       
   503     }
       
   504     return QPixmap();
       
   505 }
       
   506 
       
   507 /*!
       
   508  * Inserts/replaces the given pixmap into/in the cache if caching is enabled.
       
   509  * The pixmap will be associated with this effect instance and can be retrieved
       
   510  * later by calling cached().
       
   511  *
       
   512  * Note that the OpenVG effects rely on "out-of-sync" pixmaps, i.e. pixmaps
       
   513  * where the underlying QImage and VGImage are not synchronized. Therefore the
       
   514  * QPixmapCache must be cleared whenever there is a risk that some other entity
       
   515  * (Qt) would destroy the underlying VGImages. This is currently handled in
       
   516  * HbForegroundWatcher and HbOogmWatcher by calling releaseCachedResources().
       
   517  *
       
   518  * \sa cached()
       
   519  */
       
   520 void HbVgEffect::tryCache(const QPixmap &pm)
       
   521 {
       
   522     Q_D(HbVgEffect);
       
   523     if (d->caching) {
       
   524         QString key = cacheKey(this);
       
   525         QPixmapCache::insert(key, pm);
       
   526         cacheKeys()->insert(key);
       
   527     }
       
   528 }
       
   529 
       
   530 /*!
       
   531  * Drops all cached data used by any effect in the application. Other pixmaps
       
   532  * (not related to effects) are left intact in the global pixmap cache.
       
   533  */
       
   534 void HbVgEffect::releaseCachedResources()
       
   535 {
       
   536     QSet<QString> *keys = cacheKeys();
       
   537     foreach (const QString &key, *keys) {
       
   538         QPixmapCache::remove(key);
       
   539     }
       
   540     keys->clear();
       
   541 }
       
   542 
       
   543 /*!
       
   544  * Returns the source graphics item for this effect or null if it is not
       
   545  * available for some reason. Also works for chained effects (i.e. it uses the
       
   546  * root effect if needed).
       
   547  *
       
   548  * NOTE: If the effect has not been attached to a graphics item using install()
       
   549  * then this function returns null (even if setGraphicsEffect() was used). So
       
   550  * use install().
       
   551  */
       
   552 const QGraphicsItem *HbVgEffect::sourceItemForRoot() const
       
   553 {
       
   554     Q_D(const HbVgEffect);
       
   555     const HbVgEffectPrivate *p = d->rootEffect ? HbVgEffectPrivate::d_ptr(d->rootEffect) : d;
       
   556     return p->sourceGraphicsItem;
       
   557 }
       
   558 
       
   559 /*!
       
   560  * Returns the bounding rectangle of the source graphics item. Also works for
       
   561  * chained effects (it will use the root effect if needed).
       
   562  */
       
   563 QRectF HbVgEffect::sourceBoundingRectForRoot() const
       
   564 {
       
   565     Q_D(const HbVgEffect);
       
   566     return d->rootEffect ? d->rootEffect->sourceBoundingRect() : sourceBoundingRect();
       
   567 }
       
   568 
       
   569 /*!
       
   570  * Installs the effect on a given graphics item. This is merely a wrapper to
       
   571  * QGraphicsItem::setGraphicsEffect(). To uninstall the effect use
       
   572  * setGraphicsEffect in the usual way (i.e. pass null). This function will do
       
   573  * nothing if \a item is null.
       
   574  *
       
   575  * Note that installing effects derived from HbVgEffect using
       
   576  * QGraphicsItem::setGraphicsEffect() will not work and will lead to undefined
       
   577  * behaviour. Use this function instead. You have been warned.
       
   578  */
       
   579 void HbVgEffect::install(QGraphicsItem *item)
       
   580 {
       
   581     if (item) {
       
   582         Q_D(HbVgEffect);
       
   583         d->sourceGraphicsItem = item;
       
   584         item->setGraphicsEffect(this);
       
   585     }
       
   586 }