|
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 } |