• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                 const QRectF bounds(0, 0, m_backingStore.size.width(), m_backingStore.size.height());
648                 painter->drawPixmap(0, 0, backingStore);
649             }
650         }
651         break;
652     case PixmapContentType:
653         painter->drawPixmap(m_state.contentsRect, m_currentContent.pixmap);
654         break;
655     case ColorContentType:
656         painter->fillRect(m_state.contentsRect, m_currentContent.contentsBackgroundColor);
657         break;
658     case MediaContentType:
659         // we don't need to paint anything: we have a QGraphicsItem from the media element
660         break;
661     }
662 }
663 
notifySyncRequired()664 void GraphicsLayerQtImpl::notifySyncRequired()
665 {
666     m_blockNotifySyncRequired = false;
667 
668     if (m_layer->client())
669         m_layer->client()->notifySyncRequired(m_layer);
670 }
671 
notifyChange(ChangeMask changeMask)672 void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask)
673 {
674     m_changeMask |= changeMask;
675 
676     if (m_blockNotifySyncRequired)
677         return;
678 
679     static QMetaMethod syncMethod = staticMetaObject.method(staticMetaObject.indexOfMethod("notifySyncRequired()"));
680     syncMethod.invoke(this, Qt::QueuedConnection);
681 
682     m_blockNotifySyncRequired = true;
683 }
684 
flushChanges(bool recursive,bool forceUpdateTransform)685 void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform)
686 {
687     // This is the bulk of the work. understanding what the compositor is trying to achieve, what
688     // graphicsview can do, and trying to find a sane common-ground.
689     if (!m_layer || m_changeMask == NoChanges)
690         goto afterLayerChanges;
691 
692     if (m_changeMask & ParentChange) {
693         // The WebCore compositor manages item ownership. We have to make sure graphicsview doesn't
694         // try to snatch that ownership.
695         if (!m_layer->parent() && !parentItem())
696             setParentItem(0);
697         else if (m_layer && m_layer->parent() && m_layer->parent()->platformLayer() != parentItem())
698             setParentItem(m_layer->parent()->platformLayer());
699     }
700 
701     if (m_changeMask & ChildrenChange) {
702         // We basically do an XOR operation on the list of current children and the list of wanted
703         // children, and remove/add.
704         QSet<QGraphicsItem*> newChildren;
705         const Vector<GraphicsLayer*> newChildrenVector = (m_layer->children());
706         newChildren.reserve(newChildrenVector.size());
707 
708         for (size_t i = 0; i < newChildrenVector.size(); ++i)
709             newChildren.insert(newChildrenVector[i]->platformLayer());
710 
711         const QSet<QGraphicsItem*> currentChildren = childItems().toSet();
712         const QSet<QGraphicsItem*> childrenToAdd = newChildren - currentChildren;
713         const QSet<QGraphicsItem*> childrenToRemove = currentChildren - newChildren;
714 
715         QSet<QGraphicsItem*>::const_iterator it;
716         for (it = childrenToAdd.constBegin(); it != childrenToAdd.constEnd(); ++it) {
717              if (QGraphicsItem* w = *it)
718                 w->setParentItem(this);
719         }
720 
721         QSet<QGraphicsItem*>::const_iterator rit;
722         for (rit = childrenToRemove.constBegin(); rit != childrenToRemove.constEnd(); ++rit) {
723              if (GraphicsLayerQtImpl* w = toGraphicsLayerQtImpl(*rit))
724                 w->setParentItem(0);
725         }
726 
727         // Children are ordered by z-value, let graphicsview know.
728         for (size_t i = 0; i < newChildrenVector.size(); ++i) {
729             if (newChildrenVector[i]->platformLayer())
730                 newChildrenVector[i]->platformLayer()->setZValue(i);
731         }
732     }
733 
734     if (m_changeMask & MaskLayerChange) {
735         // We can't paint here, because we don't know if the mask layer itself is ready... we'll have
736         // to wait till this layer tries to paint.
737         setFlag(ItemClipsChildrenToShape, m_layer->maskLayer() || m_layer->masksToBounds());
738 #ifndef QT_NO_GRAPHICSEFFECT
739         setGraphicsEffect(0);
740         if (m_layer->maskLayer()) {
741             if (GraphicsLayerQtImpl* mask = toGraphicsLayerQtImpl(m_layer->maskLayer()->platformLayer())) {
742                 mask->m_maskEffect = new MaskEffectQt(this, mask);
743                 setGraphicsEffect(mask->m_maskEffect.data());
744             }
745         }
746 #endif
747     }
748 
749     if (m_changeMask & SizeChange) {
750         if (m_layer->size() != m_state.size) {
751             prepareGeometryChange();
752             m_size = QSizeF(m_layer->size().width(), m_layer->size().height());
753         }
754     }
755 
756     // FIXME: This is a hack, due to a probable QGraphicsScene bug when rapidly modifying the perspective
757     // but without this line we get graphic artifacts.
758     if ((m_changeMask & ChildrenTransformChange) && m_state.childrenTransform != m_layer->childrenTransform())
759         if (scene())
760             scene()->update();
761 
762     if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange | BackfaceVisibilityChange | PositionChange | ParentChange)) {
763         // Due to the differences between the way WebCore handles transforms and the way Qt handles transforms,
764         // all these elements affect the transforms of all the descendants.
765         forceUpdateTransform = true;
766     }
767 
768     if (m_changeMask & (ContentChange | DrawsContentChange | MaskLayerChange)) {
769         switch (m_pendingContent.contentType) {
770         case PixmapContentType:
771             update();
772             setFlag(ItemHasNoContents, false);
773             break;
774 
775         case MediaContentType:
776             setFlag(ItemHasNoContents, true);
777             m_pendingContent.mediaLayer.data()->setParentItem(this);
778             break;
779 
780         case ColorContentType:
781             if (m_pendingContent.contentType != m_currentContent.contentType
782                 || m_pendingContent.contentsBackgroundColor != m_currentContent.contentsBackgroundColor)
783                 update();
784             m_state.drawsContent = false;
785             setFlag(ItemHasNoContents, false);
786 
787             // Only use ItemUsesExtendedStyleOption for HTML content as colors don't gain much from that.
788             setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false);
789             break;
790 
791         case HTMLContentType:
792             if (m_pendingContent.contentType != m_currentContent.contentType)
793                 update();
794             if (!m_state.drawsContent && m_layer->drawsContent())
795                 update();
796 
797             setFlag(ItemHasNoContents, !m_layer->drawsContent());
798             break;
799         }
800     }
801 
802     if ((m_changeMask & OpacityChange) && m_state.opacity != m_layer->opacity() && !m_opacityAnimationRunning)
803         setOpacity(m_layer->opacity());
804 
805     if (m_changeMask & ContentsRectChange) {
806         const QRect rect(m_layer->contentsRect());
807         if (m_state.contentsRect != rect) {
808             m_state.contentsRect = rect;
809             if (m_pendingContent.mediaLayer) {
810                 QGraphicsWidget* widget = qobject_cast<QGraphicsWidget*>(m_pendingContent.mediaLayer.data());
811                 if (widget)
812                     widget->setGeometry(rect);
813             }
814             update();
815         }
816     }
817 
818     if ((m_changeMask & MasksToBoundsChange) && m_state.masksToBounds != m_layer->masksToBounds()) {
819         setFlag(QGraphicsItem::ItemClipsToShape, m_layer->masksToBounds());
820         setFlag(QGraphicsItem::ItemClipsChildrenToShape, m_layer->masksToBounds());
821     }
822 
823     if ((m_changeMask & ContentsOpaqueChange) && m_state.contentsOpaque != m_layer->contentsOpaque())
824         prepareGeometryChange();
825 
826 #ifndef QT_NO_GRAPHICSEFFECT
827     if (m_maskEffect)
828         m_maskEffect.data()->update();
829     else
830 #endif
831     if (m_changeMask & DisplayChange) {
832 #ifndef QT_GRAPHICS_LAYER_NO_RECACHE_ON_DISPLAY_CHANGE
833         // Recache now: all the content is ready and we don't want to wait until the paint event.
834         // We only need to do this for HTML content, there's no point in caching directly composited
835         // content like images or solid rectangles.
836         if (m_pendingContent.contentType == HTMLContentType && allowAcceleratedCompositingCache())
837             recache(m_pendingContent.regionToUpdate);
838 #endif
839         update(m_pendingContent.regionToUpdate.boundingRect());
840         m_pendingContent.regionToUpdate = QRegion();
841     }
842 
843     if ((m_changeMask & BackgroundColorChange)
844         && (m_pendingContent.backgroundColor != m_currentContent.backgroundColor))
845         update();
846 
847     m_state.maskLayer = m_layer->maskLayer();
848     m_state.pos = m_layer->position();
849     m_state.anchorPoint = m_layer->anchorPoint();
850     m_state.size = m_layer->size();
851     m_state.transform = m_layer->transform();
852     m_state.contentsOrientation =m_layer->contentsOrientation();
853     m_state.opacity = m_layer->opacity();
854     m_state.contentsRect = m_layer->contentsRect();
855     m_state.preserves3D = m_layer->preserves3D();
856     m_state.masksToBounds = m_layer->masksToBounds();
857     m_state.drawsContent = m_layer->drawsContent();
858     m_state.contentsOpaque = m_layer->contentsOpaque();
859     m_state.backfaceVisibility = m_layer->backfaceVisibility();
860     m_state.childrenTransform = m_layer->childrenTransform();
861     m_currentContent.pixmap = m_pendingContent.pixmap;
862     m_currentContent.contentType = m_pendingContent.contentType;
863     m_currentContent.mediaLayer = m_pendingContent.mediaLayer;
864     m_currentContent.backgroundColor = m_pendingContent.backgroundColor;
865     m_currentContent.contentsBackgroundColor = m_pendingContent.contentsBackgroundColor;
866     m_pendingContent.regionToUpdate = QRegion();
867     m_changeMask = NoChanges;
868 
869 afterLayerChanges:
870     if (forceUpdateTransform)
871         updateTransform();
872 
873     if (!recursive)
874         return;
875 
876     QList<QGraphicsItem*> children = childItems();
877     if (m_state.maskLayer)
878         children.append(m_state.maskLayer->platformLayer());
879 
880     QList<QGraphicsItem*>::const_iterator it;
881     for (it = children.constBegin(); it != children.constEnd(); ++it) {
882         if (QGraphicsItem* item = *it) {
883             if (GraphicsLayerQtImpl* layer = toGraphicsLayerQtImpl(item))
884                 layer->flushChanges(true, forceUpdateTransform);
885         }
886     }
887 }
888 
889 #if ENABLE(TILED_BACKING_STORE)
890 /* \reimp (TiledBackingStoreClient.h)
891 */
tiledBackingStorePaintBegin()892 void GraphicsLayerQtImpl::tiledBackingStorePaintBegin()
893 {
894 }
895 
896 /* \reimp (TiledBackingStoreClient.h)
897 */
tiledBackingStorePaint(GraphicsContext * gc,const IntRect & rect)898 void GraphicsLayerQtImpl::tiledBackingStorePaint(GraphicsContext* gc,  const IntRect& rect)
899 {
900     m_layer->paintGraphicsLayerContents(*gc, rect);
901 }
902 
903 /* \reimp (TiledBackingStoreClient.h)
904 */
tiledBackingStorePaintEnd(const Vector<IntRect> & paintedArea)905 void GraphicsLayerQtImpl::tiledBackingStorePaintEnd(const Vector<IntRect>& paintedArea)
906 {
907     for (int i = 0; i < paintedArea.size(); ++i)
908         update(QRectF(paintedArea[i]));
909 }
910 
911 /* \reimp (TiledBackingStoreClient.h)
912 */
tiledBackingStoreContentsRect()913 IntRect GraphicsLayerQtImpl::tiledBackingStoreContentsRect()
914 {
915     return m_layer->contentsRect();
916 }
917 
918 /* \reimp (TiledBackingStoreClient.h)
919 */
tiledBackingStoreBackgroundColor() const920 Color GraphicsLayerQtImpl::tiledBackingStoreBackgroundColor() const
921 {
922     if (m_currentContent.contentType == PixmapContentType && !m_currentContent.pixmap.hasAlphaChannel())
923         return Color(0, 0, 0);
924     // We return a transparent color so that the tiles initialize with alpha.
925     return Color(0, 0, 0, 0);
926 }
927 
tiledBackingStoreVisibleRect()928 IntRect GraphicsLayerQtImpl::tiledBackingStoreVisibleRect()
929 {
930     const QGraphicsView* view = scene()->views().isEmpty() ? 0 : scene()->views().first();
931     if (!view)
932         return mapFromScene(scene()->sceneRect()).boundingRect().toAlignedRect();
933 
934     // All we get is the viewport's visible region. We have to map it to the scene and then to item coordinates.
935     return mapFromScene(view->mapToScene(view->viewport()->visibleRegion().boundingRect()).boundingRect()).boundingRect().toAlignedRect();
936 }
937 #endif
938 
notifyAnimationStarted()939 void GraphicsLayerQtImpl::notifyAnimationStarted()
940 {
941     // WebCore notifies javascript when the animation starts. Here we're letting it know.
942     m_layer->client()->notifyAnimationStarted(m_layer, /* DOM time */ WTF::currentTime());
943 }
944 
GraphicsLayerQt(GraphicsLayerClient * client)945 GraphicsLayerQt::GraphicsLayerQt(GraphicsLayerClient* client)
946     : GraphicsLayer(client)
947     , m_impl(PassOwnPtr<GraphicsLayerQtImpl>(new GraphicsLayerQtImpl(this)))
948 {
949 }
950 
~GraphicsLayerQt()951 GraphicsLayerQt::~GraphicsLayerQt()
952 {
953 }
954 
955 // This is the hook for WebCore compositor to know that Qt implements compositing with GraphicsLayerQt.
create(GraphicsLayerClient * client)956 PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client)
957 {
958     return new GraphicsLayerQt(client);
959 }
960 
961 /* \reimp (GraphicsLayer.h): The current size might change, thus we need to update the whole display.
962 */
setNeedsDisplay()963 void GraphicsLayerQt::setNeedsDisplay()
964 {
965     m_impl->m_pendingContent.regionToUpdate = QRegion(QRect(QPoint(0, 0), QSize(size().width(), size().height())));
966     m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
967 }
968 
969 /* \reimp (GraphicsLayer.h)
970 */
setNeedsDisplayInRect(const FloatRect & rect)971 void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& rect)
972 {
973     m_impl->m_pendingContent.regionToUpdate |= QRectF(rect).toAlignedRect();
974     m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
975 }
976 
setContentsNeedsDisplay()977 void GraphicsLayerQt::setContentsNeedsDisplay()
978 {
979     switch (m_impl->m_pendingContent.contentType) {
980     case GraphicsLayerQtImpl::MediaContentType:
981         if (!m_impl->m_pendingContent.mediaLayer)
982             return;
983         m_impl->m_pendingContent.mediaLayer.data()->update();
984         break;
985     default:
986         setNeedsDisplay();
987         break;
988     }
989 }
990 
991 /* \reimp (GraphicsLayer.h)
992 */
setName(const String & name)993 void GraphicsLayerQt::setName(const String& name)
994 {
995     m_impl->setObjectName(name);
996     GraphicsLayer::setName(name);
997 }
998 
999 /* \reimp (GraphicsLayer.h)
1000 */
setParent(GraphicsLayer * layer)1001 void GraphicsLayerQt::setParent(GraphicsLayer* layer)
1002 {
1003     m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
1004     GraphicsLayer::setParent(layer);
1005 }
1006 
1007 /* \reimp (GraphicsLayer.h)
1008 */
setChildren(const Vector<GraphicsLayer * > & children)1009 bool GraphicsLayerQt::setChildren(const Vector<GraphicsLayer*>& children)
1010 {
1011     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1012     return GraphicsLayer::setChildren(children);
1013 }
1014 
1015 /* \reimp (GraphicsLayer.h)
1016 */
addChild(GraphicsLayer * layer)1017 void GraphicsLayerQt::addChild(GraphicsLayer* layer)
1018 {
1019     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1020     GraphicsLayer::addChild(layer);
1021 }
1022 
1023 /* \reimp (GraphicsLayer.h)
1024 */
addChildAtIndex(GraphicsLayer * layer,int index)1025 void GraphicsLayerQt::addChildAtIndex(GraphicsLayer* layer, int index)
1026 {
1027     GraphicsLayer::addChildAtIndex(layer, index);
1028     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1029 }
1030 
1031 /* \reimp (GraphicsLayer.h)
1032 */
addChildAbove(GraphicsLayer * layer,GraphicsLayer * sibling)1033 void GraphicsLayerQt::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling)
1034 {
1035      GraphicsLayer::addChildAbove(layer, sibling);
1036      m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1037 }
1038 
1039 /* \reimp (GraphicsLayer.h)
1040 */
addChildBelow(GraphicsLayer * layer,GraphicsLayer * sibling)1041 void GraphicsLayerQt::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling)
1042 {
1043 
1044     GraphicsLayer::addChildBelow(layer, sibling);
1045     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1046 }
1047 
1048 /* \reimp (GraphicsLayer.h)
1049 */
replaceChild(GraphicsLayer * oldChild,GraphicsLayer * newChild)1050 bool GraphicsLayerQt::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild)
1051 {
1052     if (GraphicsLayer::replaceChild(oldChild, newChild)) {
1053         m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1054         return true;
1055     }
1056 
1057     return false;
1058 }
1059 
1060 /* \reimp (GraphicsLayer.h)
1061 */
removeFromParent()1062 void GraphicsLayerQt::removeFromParent()
1063 {
1064     if (parent())
1065         m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
1066     GraphicsLayer::removeFromParent();
1067 }
1068 
1069 /* \reimp (GraphicsLayer.h)
1070 */
setMaskLayer(GraphicsLayer * value)1071 void GraphicsLayerQt::setMaskLayer(GraphicsLayer* value)
1072 {
1073     if (value == maskLayer())
1074         return;
1075     GraphicsLayer::setMaskLayer(value);
1076     m_impl->notifyChange(GraphicsLayerQtImpl::MaskLayerChange);
1077 }
1078 
1079 /* \reimp (GraphicsLayer.h)
1080 */
setPosition(const FloatPoint & value)1081 void GraphicsLayerQt::setPosition(const FloatPoint& value)
1082 {
1083     if (value == position())
1084         return;
1085     GraphicsLayer::setPosition(value);
1086     m_impl->notifyChange(GraphicsLayerQtImpl::PositionChange);
1087 }
1088 
1089 /* \reimp (GraphicsLayer.h)
1090 */
setAnchorPoint(const FloatPoint3D & value)1091 void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& value)
1092 {
1093     if (value == anchorPoint())
1094         return;
1095     GraphicsLayer::setAnchorPoint(value);
1096     m_impl->notifyChange(GraphicsLayerQtImpl::AnchorPointChange);
1097 }
1098 
1099 /* \reimp (GraphicsLayer.h)
1100 */
setSize(const FloatSize & value)1101 void GraphicsLayerQt::setSize(const FloatSize& value)
1102 {
1103     if (value == size())
1104         return;
1105     GraphicsLayer::setSize(value);
1106     m_impl->notifyChange(GraphicsLayerQtImpl::SizeChange);
1107 }
1108 
1109 /* \reimp (GraphicsLayer.h)
1110 */
setTransform(const TransformationMatrix & value)1111 void GraphicsLayerQt::setTransform(const TransformationMatrix& value)
1112 {
1113     if (value == transform())
1114         return;
1115     GraphicsLayer::setTransform(value);
1116     m_impl->notifyChange(GraphicsLayerQtImpl::TransformChange);
1117 }
1118 
1119 /* \reimp (GraphicsLayer.h)
1120 */
setChildrenTransform(const TransformationMatrix & value)1121 void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& value)
1122 {
1123     if (value == childrenTransform())
1124         return;
1125     GraphicsLayer::setChildrenTransform(value);
1126     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenTransformChange);
1127 }
1128 
1129 /* \reimp (GraphicsLayer.h)
1130 */
setPreserves3D(bool value)1131 void GraphicsLayerQt::setPreserves3D(bool value)
1132 {
1133     if (value == preserves3D())
1134         return;
1135     GraphicsLayer::setPreserves3D(value);
1136     m_impl->notifyChange(GraphicsLayerQtImpl::Preserves3DChange);
1137 }
1138 
1139 /* \reimp (GraphicsLayer.h)
1140 */
setMasksToBounds(bool value)1141 void GraphicsLayerQt::setMasksToBounds(bool value)
1142 {
1143     if (value == masksToBounds())
1144         return;
1145     GraphicsLayer::setMasksToBounds(value);
1146     m_impl->notifyChange(GraphicsLayerQtImpl::MasksToBoundsChange);
1147 }
1148 
1149 /* \reimp (GraphicsLayer.h)
1150 */
setDrawsContent(bool value)1151 void GraphicsLayerQt::setDrawsContent(bool value)
1152 {
1153     if (value == drawsContent())
1154         return;
1155     m_impl->notifyChange(GraphicsLayerQtImpl::DrawsContentChange);
1156     GraphicsLayer::setDrawsContent(value);
1157 }
1158 
1159 /* \reimp (GraphicsLayer.h)
1160 */
setBackgroundColor(const Color & value)1161 void GraphicsLayerQt::setBackgroundColor(const Color& value)
1162 {
1163     if (value == m_impl->m_pendingContent.backgroundColor)
1164         return;
1165     m_impl->m_pendingContent.backgroundColor = value;
1166     GraphicsLayer::setBackgroundColor(value);
1167     m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange);
1168 }
1169 
1170 /* \reimp (GraphicsLayer.h)
1171 */
clearBackgroundColor()1172 void GraphicsLayerQt::clearBackgroundColor()
1173 {
1174     if (!m_impl->m_pendingContent.backgroundColor.isValid())
1175         return;
1176     m_impl->m_pendingContent.backgroundColor = QColor();
1177     GraphicsLayer::clearBackgroundColor();
1178     m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange);
1179 }
1180 
1181 /* \reimp (GraphicsLayer.h)
1182 */
setContentsOpaque(bool value)1183 void GraphicsLayerQt::setContentsOpaque(bool value)
1184 {
1185     if (value == contentsOpaque())
1186         return;
1187     m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOpaqueChange);
1188     GraphicsLayer::setContentsOpaque(value);
1189 }
1190 
1191 /* \reimp (GraphicsLayer.h)
1192 */
setBackfaceVisibility(bool value)1193 void GraphicsLayerQt::setBackfaceVisibility(bool value)
1194 {
1195     if (value == backfaceVisibility())
1196         return;
1197     GraphicsLayer::setBackfaceVisibility(value);
1198     m_impl->notifyChange(GraphicsLayerQtImpl::BackfaceVisibilityChange);
1199 }
1200 
1201 /* \reimp (GraphicsLayer.h)
1202 */
setOpacity(float value)1203 void GraphicsLayerQt::setOpacity(float value)
1204 {
1205     if (value == opacity())
1206         return;
1207     GraphicsLayer::setOpacity(value);
1208     m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange);
1209 }
1210 
1211 /* \reimp (GraphicsLayer.h)
1212 */
setContentsRect(const IntRect & value)1213 void GraphicsLayerQt::setContentsRect(const IntRect& value)
1214 {
1215     if (value == contentsRect())
1216         return;
1217     GraphicsLayer::setContentsRect(value);
1218     m_impl->notifyChange(GraphicsLayerQtImpl::ContentsRectChange);
1219 }
1220 
1221 /* \reimp (GraphicsLayer.h)
1222 */
setContentsToImage(Image * image)1223 void GraphicsLayerQt::setContentsToImage(Image* image)
1224 {
1225     m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
1226     m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::HTMLContentType;
1227     GraphicsLayer::setContentsToImage(image);
1228     if (image) {
1229         QPixmap* pxm = image->nativeImageForCurrentFrame();
1230         if (pxm) {
1231             m_impl->m_pendingContent.pixmap = *pxm;
1232             m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::PixmapContentType;
1233             return;
1234         }
1235     }
1236     m_impl->m_pendingContent.pixmap = QPixmap();
1237 }
1238 
1239 /* \reimp (GraphicsLayer.h)
1240 */
setContentsBackgroundColor(const Color & color)1241 void GraphicsLayerQt::setContentsBackgroundColor(const Color& color)
1242 {
1243     m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
1244     m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::ColorContentType;
1245     m_impl->m_pendingContent.contentsBackgroundColor = QColor(color);
1246     GraphicsLayer::setContentsBackgroundColor(color);
1247 }
1248 
setContentsToMedia(PlatformLayer * media)1249 void GraphicsLayerQt::setContentsToMedia(PlatformLayer* media)
1250 {
1251     if (media) {
1252         m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::MediaContentType;
1253         m_impl->m_pendingContent.mediaLayer = media->toGraphicsObject();
1254     } else
1255         m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::HTMLContentType;
1256 
1257     m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
1258     GraphicsLayer::setContentsToMedia(media);
1259 }
1260 
setContentsToCanvas(PlatformLayer * canvas)1261 void GraphicsLayerQt::setContentsToCanvas(PlatformLayer* canvas)
1262 {
1263     setContentsToMedia(canvas);
1264 }
1265 
1266 /* \reimp (GraphicsLayer.h)
1267 */
setContentsOrientation(CompositingCoordinatesOrientation orientation)1268 void GraphicsLayerQt::setContentsOrientation(CompositingCoordinatesOrientation orientation)
1269 {
1270     m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOrientationChange);
1271     GraphicsLayer::setContentsOrientation(orientation);
1272 }
1273 
1274 /* \reimp (GraphicsLayer.h)
1275 */
distributeOpacity(float o)1276 void GraphicsLayerQt::distributeOpacity(float o)
1277 {
1278     m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange);
1279     m_impl->m_state.distributeOpacity = true;
1280 }
1281 
1282 /* \reimp (GraphicsLayer.h)
1283 */
accumulatedOpacity() const1284 float GraphicsLayerQt::accumulatedOpacity() const
1285 {
1286     return m_impl->effectiveOpacity();
1287 }
1288 
1289 /* \reimp (GraphicsLayer.h)
1290 */
syncCompositingState()1291 void GraphicsLayerQt::syncCompositingState()
1292 {
1293     m_impl->flushChanges();
1294     GraphicsLayer::syncCompositingState();
1295 }
1296 
1297 /* \reimp (GraphicsLayer.h)
1298 */
syncCompositingStateForThisLayerOnly()1299 void GraphicsLayerQt::syncCompositingStateForThisLayerOnly()
1300 {
1301     // We can't call flushChanges recursively here
1302     m_impl->flushChanges(false);
1303     GraphicsLayer::syncCompositingStateForThisLayerOnly();
1304 }
1305 
1306 /* \reimp (GraphicsLayer.h)
1307 */
platformLayer() const1308 PlatformLayer* GraphicsLayerQt::platformLayer() const
1309 {
1310     return m_impl.get();
1311 }
1312 
1313 // Now we start dealing with WebCore animations translated to Qt animations
1314 
1315 template <typename T>
1316 struct KeyframeValueQt {
1317     const TimingFunction* timingFunction;
1318     T value;
1319 };
1320 
1321 /* Copied from AnimationBase.cpp
1322 */
solveEpsilon(double duration)1323 static inline double solveEpsilon(double duration)
1324 {
1325     return 1.0 / (200.0 * duration);
1326 }
1327 
solveCubicBezierFunction(qreal p1x,qreal p1y,qreal p2x,qreal p2y,double t,double duration)1328 static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, qreal p2y, double t, double duration)
1329 {
1330     UnitBezier bezier(p1x, p1y, p2x, p2y);
1331     return bezier.solve(t, solveEpsilon(duration));
1332 }
1333 
solveStepsFunction(int numSteps,bool stepAtStart,double t)1334 static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
1335 {
1336     if (stepAtStart)
1337         return qMin(1.0, (floor(numSteps * t) + 1) / numSteps);
1338     return floor(numSteps * t) / numSteps;
1339 }
1340 
applyTimingFunction(const TimingFunction * timingFunction,qreal progress,double duration)1341 static inline qreal applyTimingFunction(const TimingFunction* timingFunction, qreal progress, double duration)
1342 {
1343     // We want the timing function to be as close as possible to what the web-developer intended, so
1344     // we're using the same function used by WebCore when compositing is disabled. Using easing-curves
1345     // would probably work for some of the cases, but wouldn't really buy us anything as we'd have to
1346     // convert the bezier function back to an easing curve.
1347 
1348     if (timingFunction->isCubicBezierTimingFunction()) {
1349         const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
1350         return solveCubicBezierFunction(ctf->x1(),
1351                                         ctf->y1(),
1352                                         ctf->x2(),
1353                                         ctf->y2(),
1354                                         double(progress), double(duration) / 1000);
1355     } else if (timingFunction->isStepsTimingFunction()) {
1356         const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(timingFunction);
1357         return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), double(progress));
1358     } else
1359         return progress;
1360 }
1361 
1362 // Helper functions to safely get a value out of WebCore's AnimationValue*.
1363 
1364 #ifndef QT_NO_ANIMATION
webkitAnimationToQtAnimationValue(const AnimationValue * animationValue,TransformOperations & transformOperations)1365 static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, TransformOperations& transformOperations)
1366 {
1367     transformOperations = TransformOperations();
1368     if (!animationValue)
1369         return;
1370 
1371     if (const TransformOperations* ops = static_cast<const TransformAnimationValue*>(animationValue)->value())
1372         transformOperations = *ops;
1373 }
1374 
webkitAnimationToQtAnimationValue(const AnimationValue * animationValue,qreal & realValue)1375 static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, qreal& realValue)
1376 {
1377     realValue = animationValue ? static_cast<const FloatAnimationValue*>(animationValue)->value() : 0;
1378 }
1379 
1380 // We put a bit of the functionality in a base class to allow casting and to save some code size.
1381 
1382 class AnimationQtBase : public QAbstractAnimation {
1383 public:
AnimationQtBase(GraphicsLayerQtImpl * layer,const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const QString & name)1384     AnimationQtBase(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
1385         : QAbstractAnimation(0)
1386         , m_layer(layer)
1387         , m_boxSize(boxSize)
1388         , m_duration(anim->duration() * 1000)
1389         , m_isAlternate(anim->direction() == Animation::AnimationDirectionAlternate)
1390         , m_webkitPropertyID(values.property())
1391         , m_webkitAnimation(anim)
1392         , m_keyframesName(name)
1393         , m_fillsForwards(false)
1394     {
1395     }
1396 
updateState(QAbstractAnimation::State newState,QAbstractAnimation::State oldState)1397     virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1398     {
1399         QAbstractAnimation::updateState(newState, oldState);
1400 
1401         // For some reason we have do this asynchronously - or the animation won't work.
1402         if (newState == Running && oldState == Stopped && m_layer.data())
1403             m_layer.data()->notifyAnimationStartedAsync();
1404     }
1405 
duration() const1406     virtual int duration() const { return m_duration; }
1407 
1408     QWeakPointer<GraphicsLayerQtImpl> m_layer;
1409     IntSize m_boxSize;
1410     int m_duration;
1411     bool m_isAlternate;
1412     AnimatedPropertyID m_webkitPropertyID;
1413 
1414     // We might need this in case the same animation is added again (i.e. resumed by WebCore).
1415     const Animation* m_webkitAnimation;
1416     QString m_keyframesName;
1417     bool m_fillsForwards;
1418 };
1419 
1420 // We'd rather have a templatized QAbstractAnimation than QPropertyAnimation / QVariantAnimation;
1421 // Since we know the types that we're dealing with, the QObject/QProperty/QVariant abstraction
1422 // buys us very little in this case, for too much overhead.
1423 template <typename T>
1424 class AnimationQt : public AnimationQtBase {
1425 
1426 public:
AnimationQt(GraphicsLayerQtImpl * layer,const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const QString & name)1427     AnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
1428         : AnimationQtBase(layer, values, boxSize, anim, name)
1429     {
1430         // Copying those WebCore structures is not trivial, we have to do it like this.
1431         for (size_t i = 0; i < values.size(); ++i) {
1432             const AnimationValue* animationValue = values.at(i);
1433             KeyframeValueQt<T> keyframeValue;
1434             if (animationValue->timingFunction())
1435                 keyframeValue.timingFunction = animationValue->timingFunction();
1436             else
1437                 keyframeValue.timingFunction = anim->timingFunction().get();
1438             webkitAnimationToQtAnimationValue(animationValue, keyframeValue.value);
1439             m_keyframeValues[animationValue->keyTime()] = keyframeValue;
1440         }
1441     }
1442 
1443 protected:
1444 
1445     // This is the part that differs between animated properties.
1446     virtual void applyFrame(const T& fromValue, const T& toValue, qreal progress) = 0;
1447 
updateState(QAbstractAnimation::State newState,QAbstractAnimation::State oldState)1448     virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1449     {
1450 #if QT_DEBUG_FPS
1451         if (newState == Running && oldState == Stopped) {
1452             qDebug("Animation Started!");
1453             m_fps.frames = 0;
1454             m_fps.duration.start();
1455         } else if (newState == Stopped && oldState == Running) {
1456             const int duration = m_fps.duration.elapsed();
1457             qDebug("Animation Ended! %dms [%f FPS]", duration,
1458                     (1000 / (((float)duration) / m_fps.frames)));
1459         }
1460 #endif
1461         AnimationQtBase::updateState(newState, oldState);
1462     }
1463 
updateCurrentTime(int currentTime)1464     virtual void updateCurrentTime(int currentTime)
1465     {
1466         if (!m_layer)
1467             return;
1468 
1469         qreal progress = qreal(currentLoopTime()) / duration();
1470 
1471         if (m_isAlternate && currentLoop()%2)
1472             progress = 1-progress;
1473 
1474         if (m_keyframeValues.isEmpty())
1475             return;
1476 
1477         // Find the current from-to keyframes in our little map.
1478         typename QMap<qreal, KeyframeValueQt<T> >::iterator it = m_keyframeValues.find(progress);
1479 
1480         // We didn't find an exact match, we try the closest match (lower bound).
1481         if (it == m_keyframeValues.end())
1482             it = m_keyframeValues.lowerBound(progress)-1;
1483 
1484         // We didn't find any match; use the first keyframe.
1485         if (it == m_keyframeValues.end())
1486             it = m_keyframeValues.begin();
1487 
1488         typename QMap<qreal, KeyframeValueQt<T> >::iterator it2 = it + 1;
1489         if (it2 == m_keyframeValues.end())
1490             it2 = it;
1491         const KeyframeValueQt<T>& fromKeyframe = it.value();
1492         const KeyframeValueQt<T>& toKeyframe = it2.value();
1493 
1494         const TimingFunction* timingFunc = fromKeyframe.timingFunction;
1495         const T& fromValue = fromKeyframe.value;
1496         const T& toValue = toKeyframe.value;
1497 
1498         // Now we have a source keyframe, origin keyframe and a timing function.
1499         // We can now process the progress and apply the frame.
1500         progress = (!progress || progress == 1 || it.key() == it2.key()) ?
1501             progress : applyTimingFunction(timingFunc, (progress - it.key()) / (it2.key() - it.key()), duration());
1502         applyFrame(fromValue, toValue, progress);
1503 #if QT_DEBUG_FPS
1504         ++m_fps.frames;
1505 #endif
1506     }
1507 
1508     QMap<qreal, KeyframeValueQt<T> > m_keyframeValues;
1509 #if QT_DEBUG_FPS
1510     struct {
1511         QTime duration;
1512         int frames;
1513     } m_fps;
1514 #endif
1515 };
1516 
1517 class TransformAnimationQt : public AnimationQt<TransformOperations> {
1518 public:
TransformAnimationQt(GraphicsLayerQtImpl * layer,const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const QString & name)1519     TransformAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
1520         : AnimationQt<TransformOperations>(layer, values, boxSize, anim, name)
1521     {
1522     }
1523 
~TransformAnimationQt()1524     ~TransformAnimationQt()
1525     {
1526         if (m_fillsForwards)
1527             setCurrentTime(1);
1528     }
1529 
1530     // The idea is that we let WebCore manage the transform operations and Qt just manage the
1531     // animation heartbeat and the bottom-line QTransform. We gain performance, not by using
1532     // Transform instead of TransformationMatrix, but by proper caching of items that are
1533     // 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)1534     virtual void applyFrame(const TransformOperations& sourceOperations, const TransformOperations& targetOperations, qreal progress)
1535     {
1536         TransformationMatrix transformMatrix;
1537 
1538         bool validTransformLists = true;
1539         const int sourceOperationCount = sourceOperations.size();
1540         if (sourceOperationCount) {
1541             if (targetOperations.size() != sourceOperationCount)
1542                 validTransformLists = false;
1543             else {
1544                 for (size_t j = 0; j < sourceOperationCount && validTransformLists; ++j) {
1545                     if (!sourceOperations.operations()[j]->isSameType(*targetOperations.operations()[j]))
1546                         validTransformLists = false;
1547                 }
1548             }
1549         }
1550 
1551         if (validTransformLists) {
1552             for (size_t i = 0; i < targetOperations.size(); ++i)
1553                 targetOperations.operations()[i]->blend(sourceOperations.at(i), progress)->apply(transformMatrix, m_boxSize);
1554         } else {
1555             targetOperations.apply(m_boxSize, transformMatrix);
1556             transformMatrix.blend(m_sourceMatrix, progress);
1557         }
1558 
1559         m_layer.data()->m_layer->setTransform(transformMatrix);
1560         // We force the actual opacity change, otherwise it would be ignored because of the animation.
1561         m_layer.data()->setBaseTransform(transformMatrix);
1562     }
1563 
updateState(QAbstractAnimation::State newState,QAbstractAnimation::State oldState)1564     virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1565     {
1566         AnimationQt<TransformOperations>::updateState(newState, oldState);
1567         if (!m_layer)
1568             return;
1569 
1570         m_layer.data()->flushChanges(true);
1571 
1572         // To increase FPS, we use a less accurate caching mechanism while animation is going on
1573         // this is a UX choice that should probably be customizable.
1574         if (newState == QAbstractAnimation::Running) {
1575             m_sourceMatrix = m_layer.data()->m_layer->transform();
1576             m_layer.data()->m_transformAnimationRunning = true;
1577         } else if (newState == QAbstractAnimation::Stopped) {
1578             // We update the transform back to the default. This already takes fill-modes into account.
1579             m_layer.data()->m_transformAnimationRunning = false;
1580             if (m_layer && m_layer.data()->m_layer)
1581                 m_layer.data()->setBaseTransform(m_layer.data()->m_layer->transform());
1582         }
1583     }
1584 
1585     TransformationMatrix m_sourceMatrix;
1586 };
1587 
1588 class OpacityAnimationQt : public AnimationQt<qreal> {
1589 public:
OpacityAnimationQt(GraphicsLayerQtImpl * layer,const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const QString & name)1590     OpacityAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString& name)
1591          : AnimationQt<qreal>(layer, values, boxSize, anim, name)
1592     {
1593     }
1594 
~OpacityAnimationQt()1595     ~OpacityAnimationQt()
1596     {
1597         if (m_fillsForwards)
1598             setCurrentTime(1);
1599     }
1600 
applyFrame(const qreal & fromValue,const qreal & toValue,qreal progress)1601     virtual void applyFrame(const qreal& fromValue, const qreal& toValue, qreal progress)
1602     {
1603         qreal opacity = qBound(qreal(0), fromValue + (toValue - fromValue) * progress, qreal(1));
1604 
1605         // FIXME: This is a hack, due to a probable QGraphicsScene bug.
1606         // Without this the opacity change doesn't always have immediate effect.
1607         if (m_layer.data()->scene() && !m_layer.data()->opacity() && opacity)
1608             m_layer.data()->scene()->update();
1609 
1610         m_layer.data()->m_layer->setOpacity(opacity);
1611         // We force the actual opacity change, otherwise it would be ignored because of the animation.
1612         m_layer.data()->setOpacity(opacity);
1613     }
1614 
updateState(QAbstractAnimation::State newState,QAbstractAnimation::State oldState)1615     virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1616     {
1617         AnimationQt<qreal>::updateState(newState, oldState);
1618 
1619         if (m_layer)
1620             m_layer.data()->m_opacityAnimationRunning = (newState == QAbstractAnimation::Running);
1621 
1622         // If stopped, we update the opacity back to the default. This already takes fill-modes into account.
1623         if (newState == Stopped)
1624             if (m_layer && m_layer.data()->m_layer)
1625                 m_layer.data()->setOpacity(m_layer.data()->m_layer->opacity());
1626 
1627     }
1628 };
1629 
addAnimation(const KeyframeValueList & values,const IntSize & boxSize,const Animation * anim,const String & keyframesName,double timeOffset)1630 bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset)
1631 {
1632     if (!anim->duration() || !anim->iterationCount())
1633         return false;
1634 
1635     AnimationQtBase* newAnim = 0;
1636 
1637     // Fixed: we might already have the Qt animation object associated with this WebCore::Animation object.
1638     QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1639     for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1640         if (*it) {
1641             AnimationQtBase* curAnimation = static_cast<AnimationQtBase*>(it->data());
1642             if (curAnimation && curAnimation->m_webkitAnimation == anim)
1643                 newAnim = curAnimation;
1644         }
1645     }
1646 
1647     if (!newAnim) {
1648         switch (values.property()) {
1649         case AnimatedPropertyOpacity:
1650             newAnim = new OpacityAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName);
1651             break;
1652         case AnimatedPropertyWebkitTransform:
1653             newAnim = new TransformAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName);
1654             break;
1655         default:
1656             return false;
1657         }
1658 
1659         // We make sure WebCore::Animation and QAnimation are on the same terms.
1660         newAnim->setLoopCount(anim->iterationCount());
1661         newAnim->m_fillsForwards = anim->fillsForwards();
1662         m_impl->m_animations.append(QWeakPointer<QAbstractAnimation>(newAnim));
1663         QObject::connect(&m_impl->m_suspendTimer, SIGNAL(timeout()), newAnim, SLOT(resume()));
1664     }
1665 
1666     // Flush now to avoid flicker.
1667     m_impl->flushChanges(false);
1668 
1669     // Qhen fill-mode is backwards/both, we set the value to 0 before the delay takes place.
1670     if (anim->fillsBackwards())
1671         newAnim->setCurrentTime(0);
1672 
1673     newAnim->start();
1674 
1675     // We synchronize the animation's clock to WebCore's timeOffset.
1676     newAnim->setCurrentTime(timeOffset * 1000);
1677 
1678     // We don't need to manage the animation object's lifecycle:
1679     // WebCore would call removeAnimations when it's time to delete.
1680 
1681     return true;
1682 }
1683 
removeAnimationsForProperty(AnimatedPropertyID id)1684 void GraphicsLayerQt::removeAnimationsForProperty(AnimatedPropertyID id)
1685 {
1686     QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1687     for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1688         if (!(*it))
1689             continue;
1690 
1691         AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data());
1692         if (anim && anim->m_webkitPropertyID == id) {
1693             // We need to stop the animation right away, or it might flicker before it's deleted.
1694             anim->stop();
1695             anim->deleteLater();
1696             it = m_impl->m_animations.erase(it);
1697             --it;
1698         }
1699     }
1700 }
1701 
removeAnimationsForKeyframes(const String & name)1702 void GraphicsLayerQt::removeAnimationsForKeyframes(const String& name)
1703 {
1704     QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1705     for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1706         if (!(*it))
1707             continue;
1708 
1709         AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data());
1710         if (anim && anim->m_keyframesName == QString(name)) {
1711             // We need to stop the animation right away, or it might flicker before it's deleted.
1712             anim->stop();
1713             anim->deleteLater();
1714             it = m_impl->m_animations.erase(it);
1715             --it;
1716         }
1717     }
1718 }
1719 
pauseAnimation(const String & name,double timeOffset)1720 void GraphicsLayerQt::pauseAnimation(const String& name, double timeOffset)
1721 {
1722     QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1723     for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1724         if (!(*it))
1725             continue;
1726 
1727         AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data());
1728         if (anim && anim->m_keyframesName == QString(name)) {
1729             // we synchronize the animation's clock to WebCore's timeOffset
1730             anim->setCurrentTime(timeOffset * 1000);
1731             anim->pause();
1732         }
1733     }
1734 }
1735 
suspendAnimations(double time)1736 void GraphicsLayerQt::suspendAnimations(double time)
1737 {
1738     if (m_impl->m_suspendTimer.isActive()) {
1739         m_impl->m_suspendTimer.stop();
1740         m_impl->m_suspendTimer.start(time * 1000);
1741     } else {
1742         QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1743         for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1744             if (QAbstractAnimation* anim = it->data())
1745                 anim->pause();
1746         }
1747     }
1748 }
1749 
resumeAnimations()1750 void GraphicsLayerQt::resumeAnimations()
1751 {
1752     if (m_impl->m_suspendTimer.isActive()) {
1753         m_impl->m_suspendTimer.stop();
1754         QList<QWeakPointer<QAbstractAnimation> >::iterator it;
1755         for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1756             if (QAbstractAnimation* anim = it->data())
1757                 anim->resume();
1758         }
1759     }
1760 }
1761 
1762 #endif // QT_NO_ANIMATION
1763 }
1764 
1765 #include <GraphicsLayerQt.moc>
1766 
1767 
1768 #endif // QT_NO_GRAPHICSVIEW
1769