1 /*
2 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include "GraphicsLayerQt.h"
22
23 #if !defined(QT_NO_GRAPHICSVIEW)
24
25 #include "CurrentTime.h"
26 #include "FloatRect.h"
27 #include "GraphicsContext.h"
28 #include "Image.h"
29 #include "RefCounted.h"
30 #include "TranslateTransformOperation.h"
31 #include "UnitBezier.h"
32 #include <QtCore/qabstractanimation.h>
33 #include <QtCore/qdatetime.h>
34 #include <QtCore/qdebug.h>
35 #include <QtCore/qmetaobject.h>
36 #include <QtCore/qset.h>
37 #include <QtCore/qtimer.h>
38 #include <QtGui/qcolor.h>
39 #include <QtGui/qgraphicseffect.h>
40 #include <QtGui/qgraphicsitem.h>
41 #include <QtGui/qgraphicsscene.h>
42 #include <QtGui/qgraphicsview.h>
43 #include <QtGui/qgraphicswidget.h>
44 #include <QtGui/qpainter.h>
45 #include <QtGui/qpixmap.h>
46 #include <QtGui/qpixmapcache.h>
47 #include <QtGui/qstyleoption.h>
48
49 #if ENABLE(TILED_BACKING_STORE)
50 #include "TiledBackingStore.h"
51 #include "TiledBackingStoreClient.h"
52
53 // The minimum width/height for tiling. We use the same value as the Windows implementation.
54 #define GRAPHICS_LAYER_TILING_THRESHOLD 2000
55 #endif
56
57 #define QT_DEBUG_RECACHE 0
58 #define QT_DEBUG_CACHEDUMP 0
59
60 #define QT_DEBUG_FPS 0
61
62 namespace WebCore {
63
64 static const int gMinimumPixmapCacheLimit = 2048;
65
66 #ifndef QT_NO_GRAPHICSEFFECT
67 class MaskEffectQt : public QGraphicsEffect {
68 public:
MaskEffectQt(QObject * parent,QGraphicsItem * maskLayer)69 MaskEffectQt(QObject* parent, QGraphicsItem* maskLayer)
70 : QGraphicsEffect(parent)
71 , m_maskLayer(maskLayer)
72 {
73 }
74
draw(QPainter * painter)75 void draw(QPainter* painter)
76 {
77 // This is a modified clone of QGraphicsOpacityEffect.
78 // It's more efficient to do it this way because:
79 // (a) We don't need the QBrush abstraction - we always end up using QGraphicsItem::paint
80 // from the mask layer.
81 // (b) QGraphicsOpacityEffect detaches the pixmap, which is inefficient on OpenGL.
82 const QSize maskSize = sourceBoundingRect().toAlignedRect().size();
83 if (!maskSize.isValid() || maskSize.isEmpty()) {
84 drawSource(painter);
85 return;
86 }
87 QPixmap maskPixmap(maskSize);
88
89 // We need to do this so the pixmap would have hasAlpha().
90 maskPixmap.fill(Qt::transparent);
91 QPainter maskPainter(&maskPixmap);
92 QStyleOptionGraphicsItem option;
93 option.exposedRect = option.rect = maskPixmap.rect();
94 maskPainter.setRenderHints(painter->renderHints(), true);
95 m_maskLayer->paint(&maskPainter, &option, 0);
96 maskPainter.end();
97
98 QPoint offset;
99 QPixmap srcPixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, QGraphicsEffect::NoPad);
100
101 // We have to use another intermediate pixmap, to make sure the mask applies only to this item
102 // and doesn't modify pixels already painted into this paint-device.
103 QPixmap pixmap(srcPixmap.size());
104 pixmap.fill(Qt::transparent);
105
106 if (pixmap.isNull())
107 return;
108
109 QPainter pixmapPainter(&pixmap);
110
111 pixmapPainter.setRenderHints(painter->renderHints());
112 pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
113
114 // We use drawPixmap rather than detaching, because it's more efficient on OpenGL.
115 pixmapPainter.drawPixmap(0, 0, srcPixmap);
116 pixmapPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
117 pixmapPainter.drawPixmap(0, 0, maskPixmap);
118
119 pixmapPainter.end();
120 painter->drawPixmap(offset, pixmap);
121 }
122
123 QGraphicsItem* m_maskLayer;
124 };
125 #endif // QT_NO_GRAPHICSEFFECT
126
127 class GraphicsLayerQtImpl : public QGraphicsObject
128 #if ENABLE(TILED_BACKING_STORE)
129 , public virtual TiledBackingStoreClient
130 #endif
131 {
132 Q_OBJECT
133
134 public:
135 // This set of flags help us defer which properties of the layer have been
136 // modified by the compositor, so we can know what to look for in the next flush.
137 enum ChangeMask {
138 NoChanges = 0,
139
140 ParentChange = (1L << 0),
141 ChildrenChange = (1L << 1),
142 MaskLayerChange = (1L << 2),
143 PositionChange = (1L << 3),
144
145 AnchorPointChange = (1L << 4),
146 SizeChange = (1L << 5),
147 TransformChange = (1L << 6),
148 ContentChange = (1L << 7),
149
150 ContentsOrientationChange = (1L << 8),
151 OpacityChange = (1L << 9),
152 ContentsRectChange = (1L << 10),
153
154 Preserves3DChange = (1L << 11),
155 MasksToBoundsChange = (1L << 12),
156 DrawsContentChange = (1L << 13),
157 ContentsOpaqueChange = (1L << 14),
158
159 BackfaceVisibilityChange = (1L << 15),
160 ChildrenTransformChange = (1L << 16),
161 DisplayChange = (1L << 17),
162 BackgroundColorChange = (1L << 18),
163
164 DistributesOpacityChange = (1L << 19)
165 };
166
167 // The compositor lets us special-case images and colors, so we try to do so.
168 enum StaticContentType { HTMLContentType, PixmapContentType, ColorContentType, MediaContentType, Canvas3DContentType};
169
170 const GraphicsLayerQtImpl* rootLayer() const;
171
172 GraphicsLayerQtImpl(GraphicsLayerQt* newLayer);
173 virtual ~GraphicsLayerQtImpl();
174
175 // reimps from QGraphicsItem
176 virtual QPainterPath opaqueArea() const;
177 virtual QRectF boundingRect() const;
178 virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
179
180 // We manage transforms ourselves because transform-origin acts differently in webkit and in Qt,
181 // and we need it as a fallback in case we encounter an un-invertible matrix.
182 void setBaseTransform(const TransformationMatrix&);
183 void updateTransform();
184
185 // let the compositor-API tell us which properties were changed
186 void notifyChange(ChangeMask);
187
188 // Actual rendering of the web-content into a QPixmap:
189 // We prefer to use our own caching because it gives us a higher level of granularity than
190 // QGraphicsItem cache modes - Sometimes we need to cache the contents even though the item
191 // needs to be updated, e.g. when the background-color is changed.
192 // TODO: investigate if QGraphicsItem caching can be improved to support that out of the box.
193 QPixmap recache(const QRegion&);
194
195 // Called when the compositor is ready for us to show the changes on screen.
196 // This is called indirectly from ChromeClientQt::setNeedsOneShotDrawingSynchronization
197 // (meaning the sync would happen together with the next draw) or
198 // ChromeClientQt::scheduleCompositingLayerSync (meaning the sync will happen ASAP)
199 void flushChanges(bool recursive = true, bool forceTransformUpdate = false);
200
201 #if ENABLE(TILED_BACKING_STORE)
202 // reimplementations from TiledBackingStoreClient
203 virtual void tiledBackingStorePaintBegin();
204 virtual void tiledBackingStorePaint(GraphicsContext*, const IntRect&);
205 virtual void tiledBackingStorePaintEnd(const Vector<IntRect>& paintedArea);
206 virtual IntRect tiledBackingStoreContentsRect();
207 virtual IntRect tiledBackingStoreVisibleRect();
208 virtual Color tiledBackingStoreBackgroundColor() const;
209 #endif
210
allowAcceleratedCompositingCache()211 static bool allowAcceleratedCompositingCache() { return QPixmapCache::cacheLimit() > gMinimumPixmapCacheLimit; }
212
213 void drawLayerContent(QPainter*, const QRect&);
214
215 public slots:
216 // We need to notify the client (ie. the layer compositor) when the animation actually starts.
217 void notifyAnimationStarted();
218
219 // We notify WebCore of a layer changed asynchronously; otherwise we end up calling flushChanges too often.
220 void notifySyncRequired();
221
222 signals:
223 // Optimization: Avoid using QTimer::singleShot().
224 void notifyAnimationStartedAsync();
225
226 public:
227 GraphicsLayerQt* m_layer;
228
229 TransformationMatrix m_baseTransform;
230 TransformationMatrix m_transformRelativeToRootLayer;
231 bool m_transformAnimationRunning;
232 bool m_opacityAnimationRunning;
233 bool m_blockNotifySyncRequired;
234 #ifndef QT_NO_GRAPHICSEFFECT
235 QWeakPointer<MaskEffectQt> m_maskEffect;
236 #endif
237
238 struct ContentData {
239 QPixmap pixmap;
240 QRegion regionToUpdate;
241 bool updateAll;
242
243 QColor contentsBackgroundColor;
244 QColor backgroundColor;
245
246 QWeakPointer<QGraphicsObject> mediaLayer;
247 StaticContentType contentType;
248
249 float opacity;
250
ContentDataWebCore::GraphicsLayerQtImpl::ContentData251 ContentData()
252 : updateAll(false)
253 , contentType(HTMLContentType)
254 , opacity(1.f)
255 {
256 }
257
258 };
259
260 ContentData m_pendingContent;
261 ContentData m_currentContent;
262
263 int m_changeMask;
264
265 #if ENABLE(TILED_BACKING_STORE)
266 TiledBackingStore* m_tiledBackingStore;
267 #endif
268
269 QSizeF m_size;
270 struct {
271 QPixmapCache::Key key;
272 QSizeF size;
273 } m_backingStore;
274 #ifndef QT_NO_ANIMATION
275 QList<QWeakPointer<QAbstractAnimation> > m_animations;
276 #endif
277 QTimer m_suspendTimer;
278
279 struct State {
280 GraphicsLayer* maskLayer;
281 FloatPoint pos;
282 FloatPoint3D anchorPoint;
283 FloatSize size;
284 TransformationMatrix transform;
285 TransformationMatrix childrenTransform;
286 Color backgroundColor;
287 Color currentColor;
288 GraphicsLayer::CompositingCoordinatesOrientation contentsOrientation;
289 float opacity;
290 QRect contentsRect;
291
292 bool preserves3D: 1;
293 bool masksToBounds: 1;
294 bool drawsContent: 1;
295 bool contentsOpaque: 1;
296 bool backfaceVisibility: 1;
297 bool distributeOpacity: 1;
298 bool align: 2;
299
StateWebCore::GraphicsLayerQtImpl::State300 State()
301 : maskLayer(0)
302 , opacity(1.f)
303 , preserves3D(false)
304 , masksToBounds(false)
305 , drawsContent(false)
306 , contentsOpaque(false)
307 , backfaceVisibility(false)
308 , distributeOpacity(false)
309 {
310 }
311 } m_state;
312
313 #ifndef QT_NO_ANIMATION
314 friend class AnimationQtBase;
315 #endif
316 };
317
toGraphicsLayerQtImpl(QGraphicsItem * item)318 inline GraphicsLayerQtImpl* toGraphicsLayerQtImpl(QGraphicsItem* item)
319 {
320 ASSERT(item);
321 return qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject());
322 }
323
toGraphicsLayerQtImpl(QGraphicsObject * item)324 inline GraphicsLayerQtImpl* toGraphicsLayerQtImpl(QGraphicsObject* item)
325 {
326 return qobject_cast<GraphicsLayerQtImpl*>(item);
327 }
328
GraphicsLayerQtImpl(GraphicsLayerQt * newLayer)329 GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer)
330 : QGraphicsObject(0)
331 , m_layer(newLayer)
332 , m_transformAnimationRunning(false)
333 , m_opacityAnimationRunning(false)
334 , m_blockNotifySyncRequired(false)
335 , m_changeMask(NoChanges)
336 #if ENABLE(TILED_BACKING_STORE)
337 , m_tiledBackingStore(0)
338 #endif
339 {
340 // We use graphics-view for compositing-only, not for interactivity.
341 setAcceptedMouseButtons(Qt::NoButton);
342
343 // We need to have the item enabled, or else wheel events are not passed to the parent class
344 // implementation of wheelEvent, where they are ignored and passed to the item below.
345 setEnabled(true);
346
347 connect(this, SIGNAL(notifyAnimationStartedAsync()), this, SLOT(notifyAnimationStarted()), Qt::QueuedConnection);
348 }
349
~GraphicsLayerQtImpl()350 GraphicsLayerQtImpl::~GraphicsLayerQtImpl()
351 {
352 // The compositor manages lifecycle of item, so we do not want the graphicsview system to delete
353 // our items automatically.
354 const QList<QGraphicsItem*> children = childItems();
355 QList<QGraphicsItem*>::const_iterator cit;
356 for (cit = children.constBegin(); cit != children.constEnd(); ++cit) {
357 if (QGraphicsItem* item = *cit) {
358 if (scene())
359 scene()->removeItem(item);
360 item->setParentItem(0);
361 }
362 }
363 #if ENABLE(TILED_BACKING_STORE)
364 delete m_tiledBackingStore;
365 #endif
366 #ifndef QT_NO_ANIMATION
367 // We do, however, own the animations.
368 QList<QWeakPointer<QAbstractAnimation> >::iterator it;
369 for (it = m_animations.begin(); it != m_animations.end(); ++it)
370 if (QAbstractAnimation* anim = it->data())
371 delete anim;
372 #endif
373 }
374
rootLayer() const375 const GraphicsLayerQtImpl* GraphicsLayerQtImpl::rootLayer() const
376 {
377 if (const GraphicsLayerQtImpl* parent = toGraphicsLayerQtImpl(parentObject()))
378 return parent->rootLayer();
379 return this;
380 }
381
382
drawLayerContent(QPainter * painter,const QRect & clipRect)383 void GraphicsLayerQtImpl::drawLayerContent(QPainter* painter, const QRect& clipRect)
384 {
385 painter->setClipRect(clipRect, Qt::IntersectClip);
386 painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
387 GraphicsContext gc(painter);
388 m_layer->paintGraphicsLayerContents(gc, clipRect);
389 }
390
recache(const QRegion & regionToUpdate)391 QPixmap GraphicsLayerQtImpl::recache(const QRegion& regionToUpdate)
392 {
393 if (!m_layer->drawsContent() || m_size.isEmpty() || !m_size.isValid())
394 return QPixmap();
395
396 #if ENABLE(TILED_BACKING_STORE)
397 const bool requiresTiling = (m_state.drawsContent && m_currentContent.contentType == HTMLContentType) && (m_size.width() > GRAPHICS_LAYER_TILING_THRESHOLD || m_size.height() > GRAPHICS_LAYER_TILING_THRESHOLD);
398 if (requiresTiling && !m_tiledBackingStore) {
399 m_tiledBackingStore = new TiledBackingStore(this);
400 m_tiledBackingStore->setTileCreationDelay(0);
401 setFlag(ItemUsesExtendedStyleOption, true);
402 } else if (!requiresTiling && m_tiledBackingStore) {
403 delete m_tiledBackingStore;
404 m_tiledBackingStore = 0;
405 setFlag(ItemUsesExtendedStyleOption, false);
406 }
407
408 if (m_tiledBackingStore) {
409 m_tiledBackingStore->adjustVisibleRect();
410 const QVector<QRect> rects = regionToUpdate.rects();
411 for (int i = 0; i < rects.size(); ++i)
412 m_tiledBackingStore->invalidate(rects[i]);
413 return QPixmap();
414 }
415 #endif
416
417 QPixmap pixmap;
418 QRegion region = regionToUpdate;
419 if (QPixmapCache::find(m_backingStore.key, &pixmap)) {
420 if (region.isEmpty())
421 return pixmap;
422 QPixmapCache::remove(m_backingStore.key); // Remove the reference to the pixmap in the cache to avoid a detach.
423 }
424
425 {
426 bool erased = false;
427
428 // If the pixmap is not in the cache or the view has grown since last cached.
429 if (pixmap.isNull() || m_size != m_backingStore.size) {
430 #if QT_DEBUG_RECACHE
431 if (pixmap.isNull())
432 qDebug() << "CacheMiss" << this << m_size;
433 #endif
434 bool fill = true;
435 QRegion newRegion;
436 QPixmap oldPixmap = pixmap;
437
438 // If the pixmap is two small to hold the view contents we enlarge, otherwise just use the old (large) pixmap.
439 if (pixmap.width() < m_size.width() || pixmap.height() < m_size.height()) {
440 #if QT_DEBUG_RECACHE
441 qDebug() << "CacheGrow" << this << m_size;
442 #endif
443 pixmap = QPixmap(m_size.toSize());
444 pixmap.fill(Qt::transparent);
445 newRegion = QRegion(0, 0, m_size.width(), m_size.height());
446 }
447
448 #if 1
449 // Blit the contents of oldPixmap back into the cached pixmap as we are just adding new pixels.
450 if (!oldPixmap.isNull()) {
451 const QRegion cleanRegion = (QRegion(0, 0, m_size.width(), m_size.height())
452 & QRegion(0, 0, m_backingStore.size.width(), m_backingStore.size.height())) - regionToUpdate;
453 if (!cleanRegion.isEmpty()) {
454 #if QT_DEBUG_RECACHE
455 qDebug() << "CacheBlit" << this << cleanRegion;
456 #endif
457 const QRect cleanBounds(cleanRegion.boundingRect());
458 QPainter painter(&pixmap);
459 painter.setCompositionMode(QPainter::CompositionMode_Source);
460 painter.drawPixmap(cleanBounds.topLeft(), oldPixmap, cleanBounds);
461 newRegion -= cleanRegion;
462 fill = false; // We cannot just fill the pixmap.
463 }
464 oldPixmap = QPixmap();
465 }
466 #endif
467 region += newRegion;
468 if (fill && !region.isEmpty()) { // Clear the entire pixmap with the background.
469 #if QT_DEBUG_RECACHE
470 qDebug() << "CacheErase" << this << m_size << background;
471 #endif
472 erased = true;
473 pixmap.fill(Qt::transparent);
474 }
475 }
476 region &= QRegion(0, 0, m_size.width(), m_size.height());
477
478 // If we have something to draw its time to erase it and render the contents.
479 if (!region.isEmpty()) {
480 #if QT_DEBUG_CACHEDUMP
481 static int recacheCount = 0;
482 ++recacheCount;
483 qDebug() << "**** CACHEDUMP" << recacheCount << this << m_layer << region << m_size;
484 pixmap.save(QString().sprintf("/tmp/%05d_A.png", recacheCount), "PNG");
485 #endif
486
487 QPainter painter(&pixmap);
488 GraphicsContext gc(&painter);
489
490 painter.setClipRegion(region);
491
492 if (!erased) { // Erase the area in cache that we're drawing into.
493 painter.setCompositionMode(QPainter::CompositionMode_Clear);
494 painter.fillRect(region.boundingRect(), Qt::transparent);
495
496 #if QT_DEBUG_CACHEDUMP
497 qDebug() << "**** CACHEDUMP" << recacheCount << this << m_layer << region << m_size;
498 pixmap.save(QString().sprintf("/tmp/%05d_B.png", recacheCount), "PNG");
499 #endif
500 }
501
502 // Render the actual contents into the cache.
503 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
504 m_layer->paintGraphicsLayerContents(gc, region.boundingRect());
505 painter.end();
506
507 #if QT_DEBUG_CACHEDUMP
508 qDebug() << "**** CACHEDUMP" << recacheCount << this << m_layer << region << m_size;
509 pixmap.save(QString().sprintf("/tmp/%05d_C.png", recacheCount), "PNG");
510 #endif
511 }
512 m_backingStore.size = m_size; // Store the used size of the pixmap.
513 }
514
515 // Finally insert into the cache and allow a reference there.
516 m_backingStore.key = QPixmapCache::insert(pixmap);
517 return pixmap;
518 }
519
updateTransform()520 void GraphicsLayerQtImpl::updateTransform()
521 {
522 if (!m_transformAnimationRunning)
523 m_baseTransform = m_layer->transform();
524
525 TransformationMatrix localTransform;
526
527 GraphicsLayerQtImpl* parent = toGraphicsLayerQtImpl(parentObject());
528
529 // WebCore has relative-to-size originPoint, where as the QGraphicsView has a pixel originPoint.
530 // Thus, we need to convert here as we have to manage this outselves due to the fact that the
531 // transformOrigin of the graphicsview is imcompatible.
532 const qreal originX = m_state.anchorPoint.x() * m_size.width();
533 const qreal originY = m_state.anchorPoint.y() * m_size.height();
534
535 // We ignore QGraphicsItem::pos completely, and use transforms only, due to the fact that we
536 // have to maintain that ourselves for 3D.
537 localTransform
538 .translate3d(originX + m_state.pos.x(), originY + m_state.pos.y(), m_state.anchorPoint.z())
539 .multiply(m_baseTransform)
540 .translate3d(-originX, -originY, -m_state.anchorPoint.z());
541
542 // This is the actual 3D transform of this item, with the ancestors' transform baked in.
543 m_transformRelativeToRootLayer = TransformationMatrix(parent ? parent->m_transformRelativeToRootLayer : TransformationMatrix())
544 .multiply(localTransform);
545
546 // Now we have enough information to determine if the layer is facing backwards.
547 if (!m_state.backfaceVisibility && m_transformRelativeToRootLayer.inverse().m33() < 0) {
548 setVisible(false);
549 // No point in making extra calculations for invisible elements.
550 return;
551 }
552
553 // The item is front-facing or backface-visibility is on.
554 setVisible(true);
555
556 // Flatten to 2D-space of this item if it doesn't preserve 3D.
557 if (!m_state.preserves3D) {
558 m_transformRelativeToRootLayer.setM13(0);
559 m_transformRelativeToRootLayer.setM23(0);
560 m_transformRelativeToRootLayer.setM31(0);
561 m_transformRelativeToRootLayer.setM32(0);
562 m_transformRelativeToRootLayer.setM33(1);
563 m_transformRelativeToRootLayer.setM34(0);
564 m_transformRelativeToRootLayer.setM43(0);
565 }
566
567 // Apply perspective for the use of this item's children. Perspective is always applied from the item's
568 // center.
569 if (!m_state.childrenTransform.isIdentity()) {
570 m_transformRelativeToRootLayer
571 .translate(m_size.width() / 2, m_size.height() /2)
572 .multiply(m_state.childrenTransform)
573 .translate(-m_size.width() / 2, -m_size.height() /2);
574 }
575
576 bool inverseOk = true;
577 // Use QTransform::inverse to extrapolate the relative transform of this item, based on the parent's
578 // transform relative to the root layer and the desired transform for this item relative to the root layer.
579 const QTransform parentTransform = parent ? parent->itemTransform(rootLayer()) : QTransform();
580 const QTransform transform2D = QTransform(m_transformRelativeToRootLayer) * parentTransform.inverted(&inverseOk);
581
582 // In rare cases the transformation cannot be inversed - in that case we don't apply the transformation at
583 // all, otherwise we'd flicker. FIXME: This should be amended when Qt moves to a real 3D scene-graph.
584 if (!inverseOk)
585 return;
586
587 setTransform(transform2D);
588
589 const QList<QGraphicsItem*> children = childItems();
590 QList<QGraphicsItem*>::const_iterator it;
591 for (it = children.constBegin(); it != children.constEnd(); ++it)
592 if (GraphicsLayerQtImpl* layer= toGraphicsLayerQtImpl(*it))
593 layer->updateTransform();
594 }
595
setBaseTransform(const TransformationMatrix & baseTransform)596 void GraphicsLayerQtImpl::setBaseTransform(const TransformationMatrix& baseTransform)
597 {
598 m_baseTransform = baseTransform;
599 updateTransform();
600 }
601
opaqueArea() const602 QPainterPath GraphicsLayerQtImpl::opaqueArea() const
603 {
604 QPainterPath painterPath;
605
606 // We try out best to return the opaque area, maybe it will help graphics-view render less items.
607 if (m_currentContent.backgroundColor.isValid() && m_currentContent.backgroundColor.alpha() == 0xff)
608 painterPath.addRect(boundingRect());
609 else {
610 if (m_state.contentsOpaque
611 || (m_currentContent.contentType == ColorContentType && m_currentContent.contentsBackgroundColor.alpha() == 0xff)
612 || (m_currentContent.contentType == MediaContentType)
613 || (m_currentContent.contentType == PixmapContentType && !m_currentContent.pixmap.hasAlpha())) {
614 painterPath.addRect(m_state.contentsRect);
615 }
616 }
617 return painterPath;
618 }
619
boundingRect() const620 QRectF GraphicsLayerQtImpl::boundingRect() const
621 {
622 return QRectF(QPointF(0, 0), QSizeF(m_size));
623 }
624
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)625 void GraphicsLayerQtImpl::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
626 {
627 #if ENABLE(TILED_BACKING_STORE)
628 // FIXME: There's currently no Qt API to know if a new region of an item is exposed outside of the paint event.
629 // Suggested for Qt: http://bugreports.qt.nokia.com/browse/QTBUG-14877.
630 if (m_tiledBackingStore)
631 m_tiledBackingStore->adjustVisibleRect();
632 #endif
633
634 if (m_currentContent.backgroundColor.isValid())
635 painter->fillRect(option->exposedRect, QColor(m_currentContent.backgroundColor));
636
637 switch (m_currentContent.contentType) {
638 case HTMLContentType:
639 if (m_state.drawsContent) {
640 if (!allowAcceleratedCompositingCache())
641 drawLayerContent(painter, option->exposedRect.toRect());
642 else {
643 QPixmap backingStore;
644 // We might need to recache, in case we try to paint and the cache was purged (e.g. if it was full).
645 if (!QPixmapCache::find(m_backingStore.key, &backingStore) || backingStore.size() != m_size.toSize())
646 backingStore = recache(QRegion(m_state.contentsRect));
647 painter->drawPixmap(0, 0, backingStore);
648 }
649 }
650 break;
651 case PixmapContentType:
652 painter->drawPixmap(m_state.contentsRect, m_currentContent.pixmap);
653 break;
654 case ColorContentType:
655 painter->fillRect(m_state.contentsRect, m_currentContent.contentsBackgroundColor);
656 break;
657 case MediaContentType:
658 // we don't need to paint anything: we have a QGraphicsItem from the media element
659 break;
660 }
661 }
662
notifySyncRequired()663 void GraphicsLayerQtImpl::notifySyncRequired()
664 {
665 m_blockNotifySyncRequired = false;
666
667 if (m_layer->client())
668 m_layer->client()->notifySyncRequired(m_layer);
669 }
670
notifyChange(ChangeMask changeMask)671 void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask)
672 {
673 m_changeMask |= changeMask;
674
675 if (m_blockNotifySyncRequired)
676 return;
677
678 static QMetaMethod syncMethod = staticMetaObject.method(staticMetaObject.indexOfMethod("notifySyncRequired()"));
679 syncMethod.invoke(this, Qt::QueuedConnection);
680
681 m_blockNotifySyncRequired = true;
682 }
683
flushChanges(bool recursive,bool forceUpdateTransform)684 void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform)
685 {
686 // This is the bulk of the work. understanding what the compositor is trying to achieve, what
687 // graphicsview can do, and trying to find a sane common-ground.
688 if (!m_layer || m_changeMask == NoChanges)
689 goto afterLayerChanges;
690
691 if (m_changeMask & ParentChange) {
692 // The WebCore compositor manages item ownership. We have to make sure graphicsview doesn't
693 // try to snatch that ownership.
694 if (!m_layer->parent() && !parentItem())
695 setParentItem(0);
696 else if (m_layer && m_layer->parent() && m_layer->parent()->platformLayer() != parentItem())
697 setParentItem(m_layer->parent()->platformLayer());
698 }
699
700 if (m_changeMask & ChildrenChange) {
701 // We basically do an XOR operation on the list of current children and the list of wanted
702 // children, and remove/add.
703 QSet<QGraphicsItem*> newChildren;
704 const Vector<GraphicsLayer*> newChildrenVector = (m_layer->children());
705 newChildren.reserve(newChildrenVector.size());
706
707 for (size_t i = 0; i < newChildrenVector.size(); ++i)
708 newChildren.insert(newChildrenVector[i]->platformLayer());
709
710 const QSet<QGraphicsItem*> currentChildren = childItems().toSet();
711 const QSet<QGraphicsItem*> childrenToAdd = newChildren - currentChildren;
712 const QSet<QGraphicsItem*> childrenToRemove = currentChildren - newChildren;
713
714 QSet<QGraphicsItem*>::const_iterator it;
715 for (it = childrenToAdd.constBegin(); it != childrenToAdd.constEnd(); ++it) {
716 if (QGraphicsItem* w = *it)
717 w->setParentItem(this);
718 }
719
720 QSet<QGraphicsItem*>::const_iterator rit;
721 for (rit = childrenToRemove.constBegin(); rit != childrenToRemove.constEnd(); ++rit) {
722 if (GraphicsLayerQtImpl* w = toGraphicsLayerQtImpl(*rit))
723 w->setParentItem(0);
724 }
725
726 // Children are ordered by z-value, let graphicsview know.
727 for (size_t i = 0; i < newChildrenVector.size(); ++i) {
728 if (newChildrenVector[i]->platformLayer())
729 newChildrenVector[i]->platformLayer()->setZValue(i);
730 }
731 }
732
733 if (m_changeMask & MaskLayerChange) {
734 // We can't paint here, because we don't know if the mask layer itself is ready... we'll have
735 // to wait till this layer tries to paint.
736 setFlag(ItemClipsChildrenToShape, m_layer->maskLayer() || m_layer->masksToBounds());
737 #ifndef QT_NO_GRAPHICSEFFECT
738 setGraphicsEffect(0);
739 if (m_layer->maskLayer()) {
740 if (GraphicsLayerQtImpl* mask = toGraphicsLayerQtImpl(m_layer->maskLayer()->platformLayer())) {
741 mask->m_maskEffect = new MaskEffectQt(this, mask);
742 setGraphicsEffect(mask->m_maskEffect.data());
743 }
744 }
745 #endif
746 }
747
748 if (m_changeMask & SizeChange) {
749 if (m_layer->size() != m_state.size) {
750 prepareGeometryChange();
751 m_size = QSizeF(m_layer->size().width(), m_layer->size().height());
752 }
753 }
754
755 // FIXME: This is a hack, due to a probable QGraphicsScene bug when rapidly modifying the perspective
756 // but without this line we get graphic artifacts.
757 if ((m_changeMask & ChildrenTransformChange) && m_state.childrenTransform != m_layer->childrenTransform())
758 if (scene())
759 scene()->update();
760
761 if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange | BackfaceVisibilityChange | PositionChange | ParentChange)) {
762 // Due to the differences between the way WebCore handles transforms and the way Qt handles transforms,
763 // all these elements affect the transforms of all the descendants.
764 forceUpdateTransform = true;
765 }
766
767 if (m_changeMask & (ContentChange | DrawsContentChange | MaskLayerChange)) {
768 switch (m_pendingContent.contentType) {
769 case PixmapContentType:
770 update();
771 setFlag(ItemHasNoContents, false);
772 break;
773
774 case MediaContentType:
775 setFlag(ItemHasNoContents, true);
776 m_pendingContent.mediaLayer.data()->setParentItem(this);
777 break;
778
779 case ColorContentType:
780 if (m_pendingContent.contentType != m_currentContent.contentType
781 || m_pendingContent.contentsBackgroundColor != m_currentContent.contentsBackgroundColor)
782 update();
783 m_state.drawsContent = false;
784 setFlag(ItemHasNoContents, false);
785
786 // Only use ItemUsesExtendedStyleOption for HTML content as colors don't gain much from that.
787 setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false);
788 break;
789
790 case HTMLContentType:
791 if (m_pendingContent.contentType != m_currentContent.contentType)
792 update();
793 else if (!m_state.drawsContent && m_layer->drawsContent())
794 update();
795
796 setFlag(ItemHasNoContents, !m_layer->drawsContent());
797 break;
798 }
799 }
800
801 if ((m_changeMask & OpacityChange) && m_state.opacity != m_layer->opacity() && !m_opacityAnimationRunning)
802 setOpacity(m_layer->opacity());
803
804 if (m_changeMask & ContentsRectChange) {
805 const QRect rect(m_layer->contentsRect());
806 if (m_state.contentsRect != rect) {
807 m_state.contentsRect = rect;
808 if (m_pendingContent.mediaLayer) {
809 QGraphicsWidget* widget = qobject_cast<QGraphicsWidget*>(m_pendingContent.mediaLayer.data());
810 if (widget)
811 widget->setGeometry(rect);
812 }
813 update();
814 }
815 }
816
817 if ((m_changeMask & MasksToBoundsChange) && m_state.masksToBounds != m_layer->masksToBounds()) {
818 setFlag(QGraphicsItem::ItemClipsToShape, m_layer->masksToBounds());
819 setFlag(QGraphicsItem::ItemClipsChildrenToShape, m_layer->masksToBounds());
820 }
821
822 if ((m_changeMask & ContentsOpaqueChange) && m_state.contentsOpaque != m_layer->contentsOpaque())
823 prepareGeometryChange();
824
825 #ifndef QT_NO_GRAPHICSEFFECT
826 if (m_maskEffect)
827 m_maskEffect.data()->update();
828 else
829 #endif
830 if (m_changeMask & DisplayChange) {
831 #ifndef QT_GRAPHICS_LAYER_NO_RECACHE_ON_DISPLAY_CHANGE
832 // Recache now: all the content is ready and we don't want to wait until the paint event.
833 // We only need to do this for HTML content, there's no point in caching directly composited
834 // content like images or solid rectangles.
835 if (m_pendingContent.contentType == HTMLContentType && allowAcceleratedCompositingCache())
836 recache(m_pendingContent.regionToUpdate);
837 #endif
838 update(m_pendingContent.regionToUpdate.boundingRect());
839 m_pendingContent.regionToUpdate = QRegion();
840 }
841
842 if ((m_changeMask & BackgroundColorChange)
843 && (m_pendingContent.backgroundColor != m_currentContent.backgroundColor))
844 update();
845
846 m_state.maskLayer = m_layer->maskLayer();
847 m_state.pos = m_layer->position();
848 m_state.anchorPoint = m_layer->anchorPoint();
849 m_state.size = m_layer->size();
850 m_state.transform = m_layer->transform();
851 m_state.contentsOrientation =m_layer->contentsOrientation();
852 m_state.opacity = m_layer->opacity();
853 m_state.contentsRect = m_layer->contentsRect();
854 m_state.preserves3D = m_layer->preserves3D();
855 m_state.masksToBounds = m_layer->masksToBounds();
856 m_state.drawsContent = m_layer->drawsContent();
857 m_state.contentsOpaque = m_layer->contentsOpaque();
858 m_state.backfaceVisibility = m_layer->backfaceVisibility();
859 m_state.childrenTransform = m_layer->childrenTransform();
860 m_currentContent.pixmap = m_pendingContent.pixmap;
861 m_currentContent.contentType = m_pendingContent.contentType;
862 m_currentContent.mediaLayer = m_pendingContent.mediaLayer;
863 m_currentContent.backgroundColor = m_pendingContent.backgroundColor;
864 m_currentContent.contentsBackgroundColor = m_pendingContent.contentsBackgroundColor;
865 m_pendingContent.regionToUpdate = QRegion();
866 m_changeMask = NoChanges;
867
868 afterLayerChanges:
869 if (forceUpdateTransform)
870 updateTransform();
871
872 if (!recursive)
873 return;
874
875 QList<QGraphicsItem*> children = childItems();
876 if (m_state.maskLayer)
877 children.append(m_state.maskLayer->platformLayer());
878
879 QList<QGraphicsItem*>::const_iterator it;
880 for (it = children.constBegin(); it != children.constEnd(); ++it) {
881 if (QGraphicsItem* item = *it) {
882 if (GraphicsLayerQtImpl* layer = toGraphicsLayerQtImpl(item))
883 layer->flushChanges(true, forceUpdateTransform);
884 }
885 }
886 }
887
888 #if ENABLE(TILED_BACKING_STORE)
889 /* \reimp (TiledBackingStoreClient.h)
890 */
tiledBackingStorePaintBegin()891 void GraphicsLayerQtImpl::tiledBackingStorePaintBegin()
892 {
893 }
894
895 /* \reimp (TiledBackingStoreClient.h)
896 */
tiledBackingStorePaint(GraphicsContext * gc,const IntRect & rect)897 void GraphicsLayerQtImpl::tiledBackingStorePaint(GraphicsContext* gc, const IntRect& rect)
898 {
899 m_layer->paintGraphicsLayerContents(*gc, rect);
900 }
901
902 /* \reimp (TiledBackingStoreClient.h)
903 */
tiledBackingStorePaintEnd(const Vector<IntRect> & paintedArea)904 void GraphicsLayerQtImpl::tiledBackingStorePaintEnd(const Vector<IntRect>& paintedArea)
905 {
906 for (int i = 0; i < paintedArea.size(); ++i)
907 update(QRectF(paintedArea[i]));
908 }
909
910 /* \reimp (TiledBackingStoreClient.h)
911 */
tiledBackingStoreContentsRect()912 IntRect GraphicsLayerQtImpl::tiledBackingStoreContentsRect()
913 {
914 return m_layer->contentsRect();
915 }
916
917 /* \reimp (TiledBackingStoreClient.h)
918 */
tiledBackingStoreBackgroundColor() const919 Color GraphicsLayerQtImpl::tiledBackingStoreBackgroundColor() const
920 {
921 if (m_currentContent.contentType == PixmapContentType && !m_currentContent.pixmap.hasAlphaChannel())
922 return Color(0, 0, 0);
923 // We return a transparent color so that the tiles initialize with alpha.
924 return Color(0, 0, 0, 0);
925 }
926
tiledBackingStoreVisibleRect()927 IntRect GraphicsLayerQtImpl::tiledBackingStoreVisibleRect()
928 {
929 const QGraphicsView* view = scene()->views().isEmpty() ? 0 : scene()->views().first();
930 if (!view)
931 return mapFromScene(scene()->sceneRect()).boundingRect().toAlignedRect();
932
933 // All we get is the viewport's visible region. We have to map it to the scene and then to item coordinates.
934 return mapFromScene(view->mapToScene(view->viewport()->visibleRegion().boundingRect()).boundingRect()).boundingRect().toAlignedRect();
935 }
936 #endif
937
notifyAnimationStarted()938 void GraphicsLayerQtImpl::notifyAnimationStarted()
939 {
940 // WebCore notifies javascript when the animation starts. Here we're letting it know.
941 m_layer->client()->notifyAnimationStarted(m_layer, /* DOM time */ WTF::currentTime());
942 }
943
GraphicsLayerQt(GraphicsLayerClient * client)944 GraphicsLayerQt::GraphicsLayerQt(GraphicsLayerClient* client)
945 : GraphicsLayer(client)
946 , m_impl(PassOwnPtr<GraphicsLayerQtImpl>(new GraphicsLayerQtImpl(this)))
947 {
948 }
949
~GraphicsLayerQt()950 GraphicsLayerQt::~GraphicsLayerQt()
951 {
952 }
953
954 // This is the hook for WebCore compositor to know that Qt implements compositing with GraphicsLayerQt.
create(GraphicsLayerClient * client)955 PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client)
956 {
957 return new GraphicsLayerQt(client);
958 }
959
960 /* \reimp (GraphicsLayer.h): The current size might change, thus we need to update the whole display.
961 */
setNeedsDisplay()962 void GraphicsLayerQt::setNeedsDisplay()
963 {
964 m_impl->m_pendingContent.regionToUpdate = QRegion(QRect(QPoint(0, 0), QSize(size().width(), size().height())));
965 m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
966 }
967
968 /* \reimp (GraphicsLayer.h)
969 */
setNeedsDisplayInRect(const FloatRect & rect)970 void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& rect)
971 {
972 m_impl->m_pendingContent.regionToUpdate |= QRectF(rect).toAlignedRect();
973 m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
974 }
975
setContentsNeedsDisplay()976 void GraphicsLayerQt::setContentsNeedsDisplay()
977 {
978 switch (m_impl->m_pendingContent.contentType) {
979 case GraphicsLayerQtImpl::MediaContentType:
980 if (!m_impl->m_pendingContent.mediaLayer)
981 return;
982 m_impl->m_pendingContent.mediaLayer.data()->update();
983 break;
984 default:
985 setNeedsDisplay();
986 break;
987 }
988 }
989
990 /* \reimp (GraphicsLayer.h)
991 */
setName(const String & name)992 void GraphicsLayerQt::setName(const String& name)
993 {
994 m_impl->setObjectName(name);
995 GraphicsLayer::setName(name);
996 }
997
998 /* \reimp (GraphicsLayer.h)
999 */
setParent(GraphicsLayer * layer)1000 void GraphicsLayerQt::setParent(GraphicsLayer* layer)
1001 {
1002 m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
1003 GraphicsLayer::setParent(layer);
1004 }
1005
1006 /* \reimp (GraphicsLayer.h)
1007 */
setChildren(const Vector<GraphicsLayer * > & children)1008 bool GraphicsLayerQt::setChildren(const Vector<GraphicsLayer*>& children)
1009 {
1010 m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1011 return GraphicsLayer::setChildren(children);
1012 }
1013
1014 /* \reimp (GraphicsLayer.h)
1015 */
addChild(GraphicsLayer * layer)1016 void GraphicsLayerQt::addChild(GraphicsLayer* layer)
1017 {
1018 m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1019 GraphicsLayer::addChild(layer);
1020 }
1021
1022 /* \reimp (GraphicsLayer.h)
1023 */
addChildAtIndex(GraphicsLayer * layer,int index)1024 void GraphicsLayerQt::addChildAtIndex(GraphicsLayer* layer, int index)
1025 {
1026 GraphicsLayer::addChildAtIndex(layer, index);
1027 m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1028 }
1029
1030 /* \reimp (GraphicsLayer.h)
1031 */
addChildAbove(GraphicsLayer * layer,GraphicsLayer * sibling)1032 void GraphicsLayerQt::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling)
1033 {
1034 GraphicsLayer::addChildAbove(layer, sibling);
1035 m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1036 }
1037
1038 /* \reimp (GraphicsLayer.h)
1039 */
addChildBelow(GraphicsLayer * layer,GraphicsLayer * sibling)1040 void GraphicsLayerQt::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling)
1041 {
1042
1043 GraphicsLayer::addChildBelow(layer, sibling);
1044 m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1045 }
1046
1047 /* \reimp (GraphicsLayer.h)
1048 */
replaceChild(GraphicsLayer * oldChild,GraphicsLayer * newChild)1049 bool GraphicsLayerQt::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild)
1050 {
1051 if (GraphicsLayer::replaceChild(oldChild, newChild)) {
1052 m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1053 return true;
1054 }
1055
1056 return false;
1057 }
1058
1059 /* \reimp (GraphicsLayer.h)
1060 */
removeFromParent()1061 void GraphicsLayerQt::removeFromParent()
1062 {
1063 if (parent())
1064 m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
1065 GraphicsLayer::removeFromParent();
1066 }
1067
1068 /* \reimp (GraphicsLayer.h)
1069 */
setMaskLayer(GraphicsLayer * value)1070 void GraphicsLayerQt::setMaskLayer(GraphicsLayer* value)
1071 {
1072 if (value == maskLayer())
1073 return;
1074 GraphicsLayer::setMaskLayer(value);
1075 m_impl->notifyChange(GraphicsLayerQtImpl::MaskLayerChange);
1076 }
1077
1078 /* \reimp (GraphicsLayer.h)
1079 */
setPosition(const FloatPoint & value)1080 void GraphicsLayerQt::setPosition(const FloatPoint& value)
1081 {
1082 if (value == position())
1083 return;
1084 GraphicsLayer::setPosition(value);
1085 m_impl->notifyChange(GraphicsLayerQtImpl::PositionChange);
1086 }
1087
1088 /* \reimp (GraphicsLayer.h)
1089 */
setAnchorPoint(const FloatPoint3D & value)1090 void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& value)
1091 {
1092 if (value == anchorPoint())
1093 return;
1094 GraphicsLayer::setAnchorPoint(value);
1095 m_impl->notifyChange(GraphicsLayerQtImpl::AnchorPointChange);
1096 }
1097
1098 /* \reimp (GraphicsLayer.h)
1099 */
setSize(const FloatSize & value)1100 void GraphicsLayerQt::setSize(const FloatSize& value)
1101 {
1102 if (value == size())
1103 return;
1104 GraphicsLayer::setSize(value);
1105 m_impl->notifyChange(GraphicsLayerQtImpl::SizeChange);
1106 }
1107
1108 /* \reimp (GraphicsLayer.h)
1109 */
setTransform(const TransformationMatrix & value)1110 void GraphicsLayerQt::setTransform(const TransformationMatrix& value)
1111 {
1112 if (value == transform())
1113 return;
1114 GraphicsLayer::setTransform(value);
1115 m_impl->notifyChange(GraphicsLayerQtImpl::TransformChange);
1116 }
1117
1118 /* \reimp (GraphicsLayer.h)
1119 */
setChildrenTransform(const TransformationMatrix & value)1120 void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& value)
1121 {
1122 if (value == childrenTransform())
1123 return;
1124 GraphicsLayer::setChildrenTransform(value);
1125 m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenTransformChange);
1126 }
1127
1128 /* \reimp (GraphicsLayer.h)
1129 */
setPreserves3D(bool value)1130 void GraphicsLayerQt::setPreserves3D(bool value)
1131 {
1132 if (value == preserves3D())
1133 return;
1134 GraphicsLayer::setPreserves3D(value);
1135 m_impl->notifyChange(GraphicsLayerQtImpl::Preserves3DChange);
1136 }
1137
1138 /* \reimp (GraphicsLayer.h)
1139 */
setMasksToBounds(bool value)1140 void GraphicsLayerQt::setMasksToBounds(bool value)
1141 {
1142 if (value == masksToBounds())
1143 return;
1144 GraphicsLayer::setMasksToBounds(value);
1145 m_impl->notifyChange(GraphicsLayerQtImpl::MasksToBoundsChange);
1146 }
1147
1148 /* \reimp (GraphicsLayer.h)
1149 */
setDrawsContent(bool value)1150 void GraphicsLayerQt::setDrawsContent(bool value)
1151 {
1152 if (value == drawsContent())
1153 return;
1154 m_impl->notifyChange(GraphicsLayerQtImpl::DrawsContentChange);
1155 GraphicsLayer::setDrawsContent(value);
1156 }
1157
1158 /* \reimp (GraphicsLayer.h)
1159 */
setBackgroundColor(const Color & value)1160 void GraphicsLayerQt::setBackgroundColor(const Color& value)
1161 {
1162 if (value == m_impl->m_pendingContent.backgroundColor)
1163 return;
1164 m_impl->m_pendingContent.backgroundColor = value;
1165 GraphicsLayer::setBackgroundColor(value);
1166 m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange);
1167 }
1168
1169 /* \reimp (GraphicsLayer.h)
1170 */
clearBackgroundColor()1171 void GraphicsLayerQt::clearBackgroundColor()
1172 {
1173 if (!m_impl->m_pendingContent.backgroundColor.isValid())
1174 return;
1175 m_impl->m_pendingContent.backgroundColor = QColor();
1176 GraphicsLayer::clearBackgroundColor();
1177 m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange);
1178 }
1179
1180 /* \reimp (GraphicsLayer.h)
1181 */
setContentsOpaque(bool value)1182 void GraphicsLayerQt::setContentsOpaque(bool value)
1183 {
1184 if (value == contentsOpaque())
1185 return;
1186 m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOpaqueChange);
1187 GraphicsLayer::setContentsOpaque(value);
1188 }
1189
1190 /* \reimp (GraphicsLayer.h)
1191 */
setBackfaceVisibility(bool value)1192 void GraphicsLayerQt::setBackfaceVisibility(bool value)
1193 {
1194 if (value == backfaceVisibility())
1195 return;
1196 GraphicsLayer::setBackfaceVisibility(value);
1197 m_impl->notifyChange(GraphicsLayerQtImpl::BackfaceVisibilityChange);
1198 }
1199
1200 /* \reimp (GraphicsLayer.h)
1201 */
setOpacity(float value)1202 void GraphicsLayerQt::setOpacity(float value)
1203 {
1204 if (value == opacity())
1205 return;
1206 GraphicsLayer::setOpacity(value);
1207 m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange);
1208 }
1209
1210 /* \reimp (GraphicsLayer.h)
1211 */
setContentsRect(const IntRect & value)1212 void GraphicsLayerQt::setContentsRect(const IntRect& value)
1213 {
1214 if (value == contentsRect())
1215 return;
1216 GraphicsLayer::setContentsRect(value);
1217 m_impl->notifyChange(GraphicsLayerQtImpl::ContentsRectChange);
1218 }
1219
1220 /* \reimp (GraphicsLayer.h)
1221 */
setContentsToImage(Image * image)1222 void GraphicsLayerQt::setContentsToImage(Image* image)
1223 {
1224 m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
1225 m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::HTMLContentType;
1226 GraphicsLayer::setContentsToImage(image);
1227 if (image) {
1228 QPixmap* pxm = image->nativeImageForCurrentFrame();
1229 if (pxm) {
1230 m_impl->m_pendingContent.pixmap = *pxm;
1231 m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::PixmapContentType;
1232 return;
1233 }
1234 }
1235 m_impl->m_pendingContent.pixmap = QPixmap();
1236 }
1237
1238 /* \reimp (GraphicsLayer.h)
1239 */
setContentsBackgroundColor(const Color & color)1240 void GraphicsLayerQt::setContentsBackgroundColor(const Color& color)
1241 {
1242 m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
1243 m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::ColorContentType;
1244 m_impl->m_pendingContent.contentsBackgroundColor = QColor(color);
1245 GraphicsLayer::setContentsBackgroundColor(color);
1246 }
1247
setContentsToMedia(PlatformLayer * media)1248 void GraphicsLayerQt::setContentsToMedia(PlatformLayer* media)
1249 {
1250 if (media) {
1251 m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::MediaContentType;
1252 m_impl->m_pendingContent.mediaLayer = media->toGraphicsObject();
1253 } else
1254 m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::HTMLContentType;
1255
1256 m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
1257 GraphicsLayer::setContentsToMedia(media);
1258 }
1259
setContentsToCanvas(PlatformLayer * canvas)1260 void GraphicsLayerQt::setContentsToCanvas(PlatformLayer* canvas)
1261 {
1262 setContentsToMedia(canvas);
1263 }
1264
1265 /* \reimp (GraphicsLayer.h)
1266 */
setContentsOrientation(CompositingCoordinatesOrientation orientation)1267 void GraphicsLayerQt::setContentsOrientation(CompositingCoordinatesOrientation orientation)
1268 {
1269 m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOrientationChange);
1270 GraphicsLayer::setContentsOrientation(orientation);
1271 }
1272
1273 /* \reimp (GraphicsLayer.h)
1274 */
distributeOpacity(float o)1275 void GraphicsLayerQt::distributeOpacity(float o)
1276 {
1277 m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange);
1278 m_impl->m_state.distributeOpacity = true;
1279 }
1280
1281 /* \reimp (GraphicsLayer.h)
1282 */
accumulatedOpacity() const1283 float GraphicsLayerQt::accumulatedOpacity() const
1284 {
1285 return m_impl->effectiveOpacity();
1286 }
1287
1288 /* \reimp (GraphicsLayer.h)
1289 */
syncCompositingState()1290 void GraphicsLayerQt::syncCompositingState()
1291 {
1292 m_impl->flushChanges();
1293 GraphicsLayer::syncCompositingState();
1294 }
1295
1296 /* \reimp (GraphicsLayer.h)
1297 */
syncCompositingStateForThisLayerOnly()1298 void GraphicsLayerQt::syncCompositingStateForThisLayerOnly()
1299 {
1300 // We can't call flushChanges recursively here
1301 m_impl->flushChanges(false);
1302 GraphicsLayer::syncCompositingStateForThisLayerOnly();
1303 }
1304
1305 /* \reimp (GraphicsLayer.h)
1306 */
platformLayer() const1307 PlatformLayer* GraphicsLayerQt::platformLayer() const
1308 {
1309 return m_impl.get();
1310 }
1311
1312 // Now we start dealing with WebCore animations translated to Qt animations
1313
1314 template <typename T>
1315 struct KeyframeValueQt {
1316 const TimingFunction* timingFunction;
1317 T value;
1318 };
1319
1320 /* Copied from AnimationBase.cpp
1321 */
solveEpsilon(double duration)1322 static inline double solveEpsilon(double duration)
1323 {
1324 return 1.0 / (200.0 * duration);
1325 }
1326
solveCubicBezierFunction(qreal p1x,qreal p1y,qreal p2x,qreal p2y,double t,double duration)1327 static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, qreal p2y, double t, double duration)
1328 {
1329 UnitBezier bezier(p1x, p1y, p2x, p2y);
1330 return bezier.solve(t, solveEpsilon(duration));
1331 }
1332
solveStepsFunction(int numSteps,bool stepAtStart,double t)1333 static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
1334 {
1335 if (stepAtStart)
1336 return qMin(1.0, (floor(numSteps * t) + 1) / numSteps);
1337 return floor(numSteps * t) / numSteps;
1338 }
1339
applyTimingFunction(const TimingFunction * timingFunction,qreal progress,double duration)1340 static inline qreal applyTimingFunction(const TimingFunction* timingFunction, qreal progress, double duration)
1341 {
1342 // We want the timing function to be as close as possible to what the web-developer intended, so
1343 // we're using the same function used by WebCore when compositing is disabled. Using easing-curves
1344 // would probably work for some of the cases, but wouldn't really buy us anything as we'd have to
1345 // convert the bezier function back to an easing curve.
1346
1347 if (timingFunction->isCubicBezierTimingFunction()) {
1348 const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
1349 return solveCubicBezierFunction(ctf->x1(),
1350 ctf->y1(),
1351 ctf->x2(),
1352 ctf->y2(),
1353 double(progress), double(duration) / 1000);
1354 } else if (timingFunction->isStepsTimingFunction()) {
1355 const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(timingFunction);
1356 return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), double(progress));
1357 } else
1358 return progress;
1359 }
1360
1361 // Helper functions to safely get a value out of WebCore's AnimationValue*.
1362
1363 #ifndef QT_NO_ANIMATION
webkitAnimationToQtAnimationValue(const AnimationValue * animationValue,TransformOperations & transformOperations)1364 static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, TransformOperations& transformOperations)
1365 {
1366 transformOperations = TransformOperations();
1367 if (!animationValue)
1368 return;
1369
1370 if (const TransformOperations* ops = static_cast<const TransformAnimationValue*>(animationValue)->value())
1371 transformOperations = *ops;
1372 }
1373
webkitAnimationToQtAnimationValue(const AnimationValue * animationValue,qreal & realValue)1374 static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, qreal& realValue)
1375 {
1376 realValue = animationValue ? static_cast<const FloatAnimationValue*>(animationValue)->value() : 0;
1377 }
1378
1379 // We put a bit of the functionality in a base class to allow casting and to save some code size.
1380
1381 class AnimationQtBase : public QAbstractAnimation {
1382 public:
AnimationQtBase(GraphicsLayerQtImpl * layer,const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const QString & name)1383 AnimationQtBase(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
1384 : QAbstractAnimation(0)
1385 , m_layer(layer)
1386 , m_boxSize(boxSize)
1387 , m_duration(anim->duration() * 1000)
1388 , m_isAlternate(anim->direction() == Animation::AnimationDirectionAlternate)
1389 , m_webkitPropertyID(values.property())
1390 , m_webkitAnimation(anim)
1391 , m_keyframesName(name)
1392 , m_fillsForwards(false)
1393 {
1394 }
1395
1396
1397 virtual AnimatedPropertyID animatedProperty() const = 0;
1398
updateState(QAbstractAnimation::State newState,QAbstractAnimation::State oldState)1399 virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1400 {
1401 QAbstractAnimation::updateState(newState, oldState);
1402
1403 // For some reason we have do this asynchronously - or the animation won't work.
1404 if (newState == Running && oldState == Stopped && m_layer.data())
1405 m_layer.data()->notifyAnimationStartedAsync();
1406 }
1407
duration() const1408 virtual int duration() const { return m_duration; }
1409
1410 QWeakPointer<GraphicsLayerQtImpl> m_layer;
1411 IntSize m_boxSize;
1412 int m_duration;
1413 bool m_isAlternate;
1414 AnimatedPropertyID m_webkitPropertyID;
1415
1416 // We might need this in case the same animation is added again (i.e. resumed by WebCore).
1417 const Animation* m_webkitAnimation;
1418 QString m_keyframesName;
1419 bool m_fillsForwards;
1420 };
1421
1422 // We'd rather have a templatized QAbstractAnimation than QPropertyAnimation / QVariantAnimation;
1423 // Since we know the types that we're dealing with, the QObject/QProperty/QVariant abstraction
1424 // buys us very little in this case, for too much overhead.
1425 template <typename T>
1426 class AnimationQt : public AnimationQtBase {
1427
1428 public:
AnimationQt(GraphicsLayerQtImpl * layer,const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const QString & name)1429 AnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
1430 : AnimationQtBase(layer, values, boxSize, anim, name)
1431 {
1432 // Copying those WebCore structures is not trivial, we have to do it like this.
1433 for (size_t i = 0; i < values.size(); ++i) {
1434 const AnimationValue* animationValue = values.at(i);
1435 KeyframeValueQt<T> keyframeValue;
1436 if (animationValue->timingFunction())
1437 keyframeValue.timingFunction = animationValue->timingFunction();
1438 else
1439 keyframeValue.timingFunction = anim->timingFunction().get();
1440 webkitAnimationToQtAnimationValue(animationValue, keyframeValue.value);
1441 m_keyframeValues[animationValue->keyTime()] = keyframeValue;
1442 }
1443 }
1444
1445 protected:
1446
1447 // This is the part that differs between animated properties.
1448 virtual void applyFrame(const T& fromValue, const T& toValue, qreal progress) = 0;
1449
updateState(QAbstractAnimation::State newState,QAbstractAnimation::State oldState)1450 virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1451 {
1452 #if QT_DEBUG_FPS
1453 if (newState == Running && oldState == Stopped) {
1454 qDebug("Animation Started!");
1455 m_fps.frames = 0;
1456 m_fps.duration.start();
1457 } else if (newState == Stopped && oldState == Running) {
1458 const int duration = m_fps.duration.elapsed();
1459 qDebug("Animation Ended! %dms [%f FPS]", duration,
1460 (1000 / (((float)duration) / m_fps.frames)));
1461 }
1462 #endif
1463 AnimationQtBase::updateState(newState, oldState);
1464 }
1465
updateCurrentTime(int currentTime)1466 virtual void updateCurrentTime(int currentTime)
1467 {
1468 if (!m_layer)
1469 return;
1470
1471 qreal progress = qreal(currentLoopTime()) / duration();
1472
1473 if (m_isAlternate && currentLoop()%2)
1474 progress = 1-progress;
1475
1476 if (m_keyframeValues.isEmpty())
1477 return;
1478
1479 // Find the current from-to keyframes in our little map.
1480 typename QMap<qreal, KeyframeValueQt<T> >::iterator it = m_keyframeValues.find(progress);
1481
1482 // We didn't find an exact match, we try the closest match (lower bound).
1483 if (it == m_keyframeValues.end())
1484 it = m_keyframeValues.lowerBound(progress)-1;
1485
1486 // We didn't find any match; use the first keyframe.
1487 if (it == m_keyframeValues.end())
1488 it = m_keyframeValues.begin();
1489
1490 typename QMap<qreal, KeyframeValueQt<T> >::iterator it2 = it + 1;
1491 if (it2 == m_keyframeValues.end())
1492 it2 = it;
1493 const KeyframeValueQt<T>& fromKeyframe = it.value();
1494 const KeyframeValueQt<T>& toKeyframe = it2.value();
1495
1496 const TimingFunction* timingFunc = fromKeyframe.timingFunction;
1497 const T& fromValue = fromKeyframe.value;
1498 const T& toValue = toKeyframe.value;
1499
1500 // Now we have a source keyframe, origin keyframe and a timing function.
1501 // We can now process the progress and apply the frame.
1502 progress = (!progress || progress == 1 || it.key() == it2.key()) ?
1503 progress : applyTimingFunction(timingFunc, (progress - it.key()) / (it2.key() - it.key()), duration());
1504 applyFrame(fromValue, toValue, progress);
1505 #if QT_DEBUG_FPS
1506 ++m_fps.frames;
1507 #endif
1508 }
1509
1510 QMap<qreal, KeyframeValueQt<T> > m_keyframeValues;
1511 #if QT_DEBUG_FPS
1512 struct {
1513 QTime duration;
1514 int frames;
1515 } m_fps;
1516 #endif
1517 };
1518
1519 class TransformAnimationQt : public AnimationQt<TransformOperations> {
1520 public:
TransformAnimationQt(GraphicsLayerQtImpl * layer,const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const QString & name)1521 TransformAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
1522 : AnimationQt<TransformOperations>(layer, values, boxSize, anim, name)
1523 {
1524 }
1525
~TransformAnimationQt()1526 ~TransformAnimationQt()
1527 {
1528 if (m_fillsForwards)
1529 setCurrentTime(1);
1530 }
1531
animatedProperty() const1532 virtual AnimatedPropertyID animatedProperty() const { return AnimatedPropertyWebkitTransform; }
1533
1534 // The idea is that we let WebCore manage the transform operations and Qt just manage the
1535 // animation heartbeat and the bottom-line QTransform. We gain performance, not by using
1536 // Transform instead of TransformationMatrix, but by proper caching of items that are
1537 // expensive for WebCore to render. We want the rest to be as close to WebCore's idea as possible.
applyFrame(const TransformOperations & sourceOperations,const TransformOperations & targetOperations,qreal progress)1538 virtual void applyFrame(const TransformOperations& sourceOperations, const TransformOperations& targetOperations, qreal progress)
1539 {
1540 TransformationMatrix transformMatrix;
1541
1542 bool validTransformLists = true;
1543 const int sourceOperationCount = sourceOperations.size();
1544 if (sourceOperationCount) {
1545 if (targetOperations.size() != sourceOperationCount)
1546 validTransformLists = false;
1547 else {
1548 for (size_t j = 0; j < sourceOperationCount && validTransformLists; ++j) {
1549 if (!sourceOperations.operations()[j]->isSameType(*targetOperations.operations()[j]))
1550 validTransformLists = false;
1551 }
1552 }
1553 }
1554
1555 if (validTransformLists) {
1556 for (size_t i = 0; i < targetOperations.size(); ++i)
1557 targetOperations.operations()[i]->blend(sourceOperations.at(i), progress)->apply(transformMatrix, m_boxSize);
1558 } else {
1559 targetOperations.apply(m_boxSize, transformMatrix);
1560 transformMatrix.blend(m_sourceMatrix, progress);
1561 }
1562
1563 m_layer.data()->m_layer->setTransform(transformMatrix);
1564 // We force the actual opacity change, otherwise it would be ignored because of the animation.
1565 m_layer.data()->setBaseTransform(transformMatrix);
1566 }
1567
updateState(QAbstractAnimation::State newState,QAbstractAnimation::State oldState)1568 virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1569 {
1570 AnimationQt<TransformOperations>::updateState(newState, oldState);
1571 if (!m_layer)
1572 return;
1573
1574 m_layer.data()->flushChanges(true);
1575
1576 // To increase FPS, we use a less accurate caching mechanism while animation is going on
1577 // this is a UX choice that should probably be customizable.
1578 if (newState == QAbstractAnimation::Running) {
1579 m_sourceMatrix = m_layer.data()->m_layer->transform();
1580 m_layer.data()->m_transformAnimationRunning = true;
1581 } else if (newState == QAbstractAnimation::Stopped) {
1582 // We update the transform back to the default. This already takes fill-modes into account.
1583 m_layer.data()->m_transformAnimationRunning = false;
1584 if (m_layer && m_layer.data()->m_layer)
1585 m_layer.data()->setBaseTransform(m_layer.data()->m_layer->transform());
1586 }
1587 }
1588
1589 TransformationMatrix m_sourceMatrix;
1590 };
1591
1592 class OpacityAnimationQt : public AnimationQt<qreal> {
1593 public:
OpacityAnimationQt(GraphicsLayerQtImpl * layer,const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const QString & name)1594 OpacityAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString& name)
1595 : AnimationQt<qreal>(layer, values, boxSize, anim, name)
1596 {
1597 }
1598
~OpacityAnimationQt()1599 ~OpacityAnimationQt()
1600 {
1601 if (m_fillsForwards)
1602 setCurrentTime(1);
1603 }
1604
animatedProperty() const1605 virtual AnimatedPropertyID animatedProperty() const { return AnimatedPropertyOpacity; }
1606
applyFrame(const qreal & fromValue,const qreal & toValue,qreal progress)1607 virtual void applyFrame(const qreal& fromValue, const qreal& toValue, qreal progress)
1608 {
1609 qreal opacity = qBound(qreal(0), fromValue + (toValue - fromValue) * progress, qreal(1));
1610
1611 // FIXME: This is a hack, due to a probable QGraphicsScene bug.
1612 // Without this the opacity change doesn't always have immediate effect.
1613 if (m_layer.data()->scene() && !m_layer.data()->opacity() && opacity)
1614 m_layer.data()->scene()->update();
1615
1616 m_layer.data()->m_layer->setOpacity(opacity);
1617 // We force the actual opacity change, otherwise it would be ignored because of the animation.
1618 m_layer.data()->setOpacity(opacity);
1619 }
1620
updateState(QAbstractAnimation::State newState,QAbstractAnimation::State oldState)1621 virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1622 {
1623 AnimationQt<qreal>::updateState(newState, oldState);
1624
1625 if (m_layer)
1626 m_layer.data()->m_opacityAnimationRunning = (newState == QAbstractAnimation::Running);
1627
1628 // If stopped, we update the opacity back to the default. This already takes fill-modes into account.
1629 if (newState == Stopped)
1630 if (m_layer && m_layer.data()->m_layer)
1631 m_layer.data()->setOpacity(m_layer.data()->m_layer->opacity());
1632
1633 }
1634 };
1635
addAnimation(const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const String & keyframesName,double timeOffset)1636 bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset)
1637 {
1638 if (!anim->duration() || !anim->iterationCount())
1639 return false;
1640
1641 AnimationQtBase* newAnim = 0;
1642
1643 // Fixed: we might already have the Qt animation object associated with this WebCore::Animation object.
1644 QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1645 for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1646 if (*it) {
1647 AnimationQtBase* curAnimation = static_cast<AnimationQtBase*>(it->data());
1648 if (curAnimation && curAnimation->m_webkitAnimation == anim
1649 && values.property() == curAnimation->animatedProperty()) {
1650 newAnim = curAnimation;
1651 break;
1652 }
1653 }
1654 }
1655
1656 if (!newAnim) {
1657 switch (values.property()) {
1658 case AnimatedPropertyOpacity:
1659 newAnim = new OpacityAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName);
1660 break;
1661 case AnimatedPropertyWebkitTransform:
1662 newAnim = new TransformAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName);
1663 break;
1664 default:
1665 return false;
1666 }
1667
1668 // We make sure WebCore::Animation and QAnimation are on the same terms.
1669 newAnim->setLoopCount(anim->iterationCount());
1670 newAnim->m_fillsForwards = anim->fillsForwards();
1671 m_impl->m_animations.append(QWeakPointer<QAbstractAnimation>(newAnim));
1672 QObject::connect(&m_impl->m_suspendTimer, SIGNAL(timeout()), newAnim, SLOT(resume()));
1673 }
1674
1675 // Flush now to avoid flicker.
1676 m_impl->flushChanges(false);
1677
1678 // Qhen fill-mode is backwards/both, we set the value to 0 before the delay takes place.
1679 if (anim->fillsBackwards())
1680 newAnim->setCurrentTime(0);
1681
1682 newAnim->start();
1683
1684 // We synchronize the animation's clock to WebCore's timeOffset.
1685 newAnim->setCurrentTime(timeOffset * 1000);
1686
1687 // We don't need to manage the animation object's lifecycle:
1688 // WebCore would call removeAnimations when it's time to delete.
1689
1690 return true;
1691 }
1692
removeAnimationsForProperty(AnimatedPropertyID id)1693 void GraphicsLayerQt::removeAnimationsForProperty(AnimatedPropertyID id)
1694 {
1695 QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1696 for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1697 if (!(*it))
1698 continue;
1699
1700 AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data());
1701 if (anim && anim->m_webkitPropertyID == id) {
1702 // We need to stop the animation right away, or it might flicker before it's deleted.
1703 anim->stop();
1704 anim->deleteLater();
1705 it = m_impl->m_animations.erase(it);
1706 --it;
1707 }
1708 }
1709 }
1710
removeAnimationsForKeyframes(const String & name)1711 void GraphicsLayerQt::removeAnimationsForKeyframes(const String& name)
1712 {
1713 QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1714 for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1715 if (!(*it))
1716 continue;
1717
1718 AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data());
1719 if (anim && anim->m_keyframesName == QString(name)) {
1720 // We need to stop the animation right away, or it might flicker before it's deleted.
1721 anim->stop();
1722 anim->deleteLater();
1723 it = m_impl->m_animations.erase(it);
1724 --it;
1725 }
1726 }
1727 }
1728
pauseAnimation(const String & name,double timeOffset)1729 void GraphicsLayerQt::pauseAnimation(const String& name, double timeOffset)
1730 {
1731 QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1732 for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1733 if (!(*it))
1734 continue;
1735
1736 AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data());
1737 if (anim && anim->m_keyframesName == QString(name)) {
1738 // we synchronize the animation's clock to WebCore's timeOffset
1739 anim->setCurrentTime(timeOffset * 1000);
1740 anim->pause();
1741 }
1742 }
1743 }
1744
suspendAnimations(double time)1745 void GraphicsLayerQt::suspendAnimations(double time)
1746 {
1747 if (m_impl->m_suspendTimer.isActive()) {
1748 m_impl->m_suspendTimer.stop();
1749 m_impl->m_suspendTimer.start(time * 1000);
1750 } else {
1751 QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1752 for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1753 if (QAbstractAnimation* anim = it->data())
1754 anim->pause();
1755 }
1756 }
1757 }
1758
resumeAnimations()1759 void GraphicsLayerQt::resumeAnimations()
1760 {
1761 if (m_impl->m_suspendTimer.isActive()) {
1762 m_impl->m_suspendTimer.stop();
1763 QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1764 for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1765 if (QAbstractAnimation* anim = it->data())
1766 anim->resume();
1767 }
1768 }
1769 }
1770
1771 #endif // QT_NO_ANIMATION
1772 }
1773
1774 #include <GraphicsLayerQt.moc>
1775
1776
1777 #endif // QT_NO_GRAPHICSVIEW
1778