2010-02-17 Noam Rosenthal <noam.rosenthal@nokia.com>
[WebKit-https.git] / WebCore / platform / graphics / qt / GraphicsLayerQt.cpp
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 #include "CurrentTime.h"
24 #include "FloatRect.h"
25 #include "GraphicsContext.h"
26 #include "Image.h"
27 #include "RefCounted.h"
28 #include "TranslateTransformOperation.h"
29 #include "UnitBezier.h"
30 #include <QtCore/qabstractanimation.h>
31 #include <QtCore/qdebug.h>
32 #include <QtCore/qset.h>
33 #include <QtCore/qtimer.h>
34 #include <QtGui/qbitmap.h>
35 #include <QtGui/qcolor.h>
36 #include <QtGui/qgraphicseffect.h>
37 #include <QtGui/qgraphicsitem.h>
38 #include <QtGui/qgraphicsscene.h>
39 #include <QtGui/qmatrix4x4.h>
40 #include <QtGui/qpainter.h>
41 #include <QtGui/qpalette.h>
42 #include <QtGui/qpixmap.h>
43 #include <QtGui/qstyleoption.h>
44
45 namespace WebCore {
46
47 class MaskEffectQt : public QGraphicsEffect {
48 public:
49     MaskEffectQt(QObject* parent, QGraphicsItem* maskLayer)
50             : QGraphicsEffect(parent)
51             , m_maskLayer(maskLayer)
52     {
53     }
54
55     void draw(QPainter* painter)
56     {
57         // this is a modified clone of QGraphicsOpacityEffect.
58         // It's more efficient to do it this way because
59         // (a) we don't need the QBrush abstraction - we always end up using QGraphicsItem::paint from the mask layer
60         // (b) QGraphicsOpacityEffect detaches the pixmap, which is inefficient on OpenGL.
61         QPixmap maskPixmap(sourceBoundingRect().toAlignedRect().size());
62
63         // we need to do this so the pixmap would have hasAlpha()
64         maskPixmap.fill(Qt::transparent);
65         QPainter maskPainter(&maskPixmap);
66         QStyleOptionGraphicsItem option;
67         option.exposedRect = option.rect = maskPixmap.rect();
68         maskPainter.setRenderHints(painter->renderHints(), true);
69         m_maskLayer->paint(&maskPainter, &option, 0);
70         maskPainter.end();
71         QPoint offset;
72         QPixmap srcPixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, QGraphicsEffect::NoPad);
73
74         // we have to use another intermediate pixmap, to make sure the mask applies only to this item
75         // and doesn't modify pixels already painted into this paint-device
76         QPixmap pixmap(srcPixmap.size());
77         pixmap.fill(Qt::transparent);
78
79         if (pixmap.isNull())
80             return;
81
82         QPainter pixmapPainter(&pixmap);
83         pixmapPainter.setRenderHints(painter->renderHints());
84         pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
85         // We use drawPixmap rather than detaching, because it's more efficient on OpenGL
86         pixmapPainter.drawPixmap(0, 0, srcPixmap);
87         pixmapPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
88         pixmapPainter.drawPixmap(0, 0, maskPixmap);
89         pixmapPainter.end();
90         painter->drawPixmap(offset, pixmap);
91     }
92
93     QGraphicsItem* m_maskLayer;
94 };
95
96 class GraphicsLayerQtImpl : public QGraphicsObject {
97     Q_OBJECT
98
99 public:
100     // this set of flags help us defer which properties of the layer have been
101     // modified by the compositor, so we can know what to look for in the next flush
102     enum ChangeMask {
103         NoChanges =                 0,
104         ChildrenChange =            (1L << 1),
105         MaskLayerChange =           (1L << 2),
106         PositionChange =            (1L << 3),
107         AnchorPointChange =         (1L << 4),
108         SizeChange  =               (1L << 5),
109         TransformChange =           (1L << 6),
110         ContentChange =             (1L << 7),
111         GeometryOrientationChange = (1L << 8),
112         ContentsOrientationChange = (1L << 9),
113         OpacityChange =             (1L << 10),
114         ContentsRectChange =        (1L << 11),
115         Preserves3DChange =         (1L << 12),
116         MasksToBoundsChange =       (1L << 13),
117         DrawsContentChange =        (1L << 14),
118         ContentsOpaqueChange =      (1L << 15),
119         BackfaceVisibilityChange =  (1L << 16),
120         ChildrenTransformChange =   (1L << 17),
121         DisplayChange =             (1L << 18),
122         BackgroundColorChange =     (1L << 19),
123         ParentChange =              (1L << 20),
124         DistributesOpacityChange =  (1L << 21)
125     };
126
127     // the compositor lets us special-case images and colors, so we try to do so
128     enum StaticContentType { HTMLContentType, PixmapContentType, ColorContentType};
129
130     GraphicsLayerQtImpl(GraphicsLayerQt* newLayer);
131     virtual ~GraphicsLayerQtImpl();
132
133     // reimps from QGraphicsItem
134     virtual QPainterPath opaqueArea() const;
135     virtual QRectF boundingRect() const;
136     virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
137
138     // we manage transforms ourselves because transform-origin acts differently in webkit and in Qt
139     void setBaseTransform(const TransformationMatrix&);
140     QTransform computeTransform(const TransformationMatrix& baseTransform) const;
141     void updateTransform();
142
143     // let the compositor-API tell us which properties were changed
144     void notifyChange(ChangeMask);
145
146     // called when the compositor is ready for us to show the changes on screen
147     // this is called indirectly from ChromeClientQt::setNeedsOneShotDrawingSynchronization
148     // (meaning the sync would happen together with the next draw)
149     // or ChromeClientQt::scheduleCompositingLayerSync (meaning the sync will happen ASAP)
150     void flushChanges(bool recursive = true, bool forceTransformUpdate = false);
151
152     // optimization: when we have an animation running on an element with no contents, that has child-elements with contents,
153     // ALL of them have to have ItemCoordinateCache and not DeviceCoordinateCache
154     void adjustCachingRecursively(bool animationIsRunning);
155
156     // optimization: returns true if this or an ancestor has a transform animation running.
157     // this enables us to use ItemCoordinatesCache while the animation is running, otherwise we have to recache for every frame
158     bool isTransformAnimationRunning() const;
159
160 public slots:
161     // we need to notify the client (aka the layer compositor) when the animation actually starts
162     void notifyAnimationStarted();
163
164 signals:
165     // optimization: we don't want to use QTimer::singleShot
166     void notifyAnimationStartedAsync();
167
168 public:
169     GraphicsLayerQt* m_layer;
170
171     TransformationMatrix m_baseTransform;
172     bool m_transformAnimationRunning;
173     bool m_opacityAnimationRunning;
174     QWeakPointer<MaskEffectQt> m_maskEffect;
175
176     struct ContentData {
177         QPixmap pixmap;
178         QRegion regionToUpdate;
179         bool updateAll;
180         QColor contentsBackgroundColor;
181         QColor backgroundColor;
182         StaticContentType contentType;
183         float opacity;
184         ContentData()
185                 : updateAll(false)
186                 , contentType(HTMLContentType)
187                 , opacity(1.f)
188         {
189         }
190
191     };
192
193     ContentData m_pendingContent;
194     ContentData m_currentContent;
195
196     int m_changeMask;
197
198     QSizeF m_size;
199     QList<QWeakPointer<QAbstractAnimation> > m_animations;
200     QTimer m_suspendTimer;
201
202     struct State {
203         GraphicsLayer* maskLayer;
204         FloatPoint pos;
205         FloatPoint3D anchorPoint;
206         FloatSize size;
207         TransformationMatrix transform;
208         TransformationMatrix childrenTransform;
209         Color backgroundColor;
210         Color currentColor;
211         GraphicsLayer::CompositingCoordinatesOrientation geoOrientation;
212         GraphicsLayer::CompositingCoordinatesOrientation contentsOrientation;
213         float opacity;
214         QRect contentsRect;
215
216         bool preserves3D: 1;
217         bool masksToBounds: 1;
218         bool drawsContent: 1;
219         bool contentsOpaque: 1;
220         bool backfaceVisibility: 1;
221         bool distributeOpacity: 1;
222         bool align: 2;
223         State(): maskLayer(0), opacity(1.f), preserves3D(false), masksToBounds(false),
224                   drawsContent(false), contentsOpaque(false), backfaceVisibility(false),
225                   distributeOpacity(false)
226         {
227         }
228     } m_state;
229
230     friend class AnimationQtBase;
231 };
232
233 GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer)
234     : QGraphicsObject(0)
235     , m_layer(newLayer)
236     , m_transformAnimationRunning(false)
237     , m_opacityAnimationRunning(false)
238     , m_changeMask(NoChanges)
239 {
240     // we use graphics-view for compositing, not for interactivity
241     setAcceptedMouseButtons(Qt::NoButton);
242     setEnabled(false);
243
244     // we'll set the cache when we know what's going on
245     setCacheMode(NoCache);
246
247     connect(this, SIGNAL(notifyAnimationStartedAsync()), this, SLOT(notifyAnimationStarted()), Qt::QueuedConnection);
248 }
249
250 GraphicsLayerQtImpl::~GraphicsLayerQtImpl()
251 {
252     // the compositor manages item lifecycle - we don't want the graphics-view
253     // system to automatically delete our items
254
255     const QList<QGraphicsItem*> children = childItems();
256     for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) {
257         if (QGraphicsItem* item = *it) {
258             if (scene())
259                 scene()->removeItem(item);
260             item->setParentItem(0);
261         }
262     }
263     
264     // we do, however, own the animations...
265     for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_animations.begin(); it != m_animations.end(); ++it)
266         if (QAbstractAnimation* anim = it->data())
267             delete anim;
268 }
269
270 void GraphicsLayerQtImpl::adjustCachingRecursively(bool animationIsRunning)
271 {
272     // optimization: we make sure all our children have ItemCoordinateCache -
273     // otherwise we end up re-rendering them during the animation
274     const QList<QGraphicsItem*> children = childItems();
275
276     for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) {
277         if (QGraphicsItem* item = *it)
278             if (GraphicsLayerQtImpl* layer = qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject())) {
279                 if (layer->m_layer->drawsContent() && layer->m_currentContent.contentType == HTMLContentType)
280                     layer->setCacheMode(animationIsRunning ? QGraphicsItem::ItemCoordinateCache : QGraphicsItem::DeviceCoordinateCache);
281             }
282     }    
283 }
284
285 void GraphicsLayerQtImpl::updateTransform()
286 {
287     setBaseTransform(isTransformAnimationRunning() ? m_baseTransform : m_layer->transform());
288 }
289
290 void GraphicsLayerQtImpl::setBaseTransform(const TransformationMatrix& baseTransform)
291 {
292     m_baseTransform = baseTransform;
293     setTransform(computeTransform(baseTransform));
294 }
295
296 QTransform GraphicsLayerQtImpl::computeTransform(const TransformationMatrix& baseTransform) const
297 {
298     if (!m_layer)
299         return baseTransform;
300
301     TransformationMatrix computedTransform;
302
303     // The origin for childrenTransform is always the center of the ancestor which contains the childrenTransform.
304     // this has to do with how WebCore implements -webkit-perspective and -webkit-perspective-origin, which are the CSS
305     // attribute that call setChildrenTransform
306     QPointF offset = -pos() - boundingRect().bottomRight() / 2;
307     const GraphicsLayerQtImpl* ancestor = this;
308     while ((ancestor = qobject_cast<GraphicsLayerQtImpl*>(ancestor->parentObject()))) {
309         if (!ancestor->m_state.childrenTransform.isIdentity()) {
310             offset += ancestor->boundingRect().bottomRight() / 2;
311             computedTransform
312                 .translate(offset.x(), offset.y())
313                 .multLeft(ancestor->m_state.childrenTransform)
314                 .translate(-offset.x(), -offset.y());
315             break;
316         }
317         offset -= ancestor->pos();
318     }
319
320     computedTransform.multLeft(baseTransform);
321
322     // webkit has relative-to-size originPoint, graphics-view has a pixel originPoint, here we convert
323     // we have to manage this ourselves because QGraphicsView's transformOrigin is incompatible
324     const qreal originX = m_state.anchorPoint.x() * m_size.width();
325     const qreal originY = m_state.anchorPoint.y() * m_size.height();
326     computedTransform = TransformationMatrix()
327                             .translate(originX, originY)
328                             .multiply(computedTransform)
329                             .translate(-originX, -originY);
330
331     // now we project to 2D
332     return QTransform(computedTransform);
333 }
334
335 bool GraphicsLayerQtImpl::isTransformAnimationRunning() const
336 {
337     if (m_transformAnimationRunning)
338         return true;
339     if (GraphicsLayerQtImpl* parent = qobject_cast<GraphicsLayerQtImpl*>(parentObject()))
340         return parent->isTransformAnimationRunning();
341     return false;
342 }
343
344 QPainterPath GraphicsLayerQtImpl::opaqueArea() const
345 {
346     QPainterPath painterPath;
347     // we try out best to return the opaque area, maybe it will help graphics-view render less items
348     if (m_currentContent.backgroundColor.isValid() && m_currentContent.backgroundColor.alpha() == 0xff)
349         painterPath.addRect(boundingRect());
350     else {
351         if (m_state.contentsOpaque
352             || (m_currentContent.contentType == ColorContentType && m_currentContent.contentsBackgroundColor.alpha() == 0xff)
353             || (m_currentContent.contentType == PixmapContentType && !m_currentContent.pixmap.hasAlpha())) {
354
355             painterPath.addRect(m_state.contentsRect);
356         }
357     }
358     return painterPath;
359 }
360
361 QRectF GraphicsLayerQtImpl::boundingRect() const
362 {
363     return QRectF(QPointF(0, 0), QSizeF(m_size));
364 }
365
366 void GraphicsLayerQtImpl::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
367 {
368     if (m_currentContent.backgroundColor.isValid())
369         painter->fillRect(option->exposedRect, QColor(m_currentContent.backgroundColor));
370
371     switch (m_currentContent.contentType) {
372     case HTMLContentType:
373         if (m_state.drawsContent) {
374             // this is the expensive bit. we try to minimize calls to this area by proper caching
375             GraphicsContext gc(painter);
376             m_layer->paintGraphicsLayerContents(gc, option->exposedRect.toAlignedRect());
377         }
378         break;
379     case PixmapContentType:
380         painter->drawPixmap(m_state.contentsRect, m_currentContent.pixmap);
381         break;
382     case ColorContentType:
383         painter->fillRect(m_state.contentsRect, m_currentContent.contentsBackgroundColor);
384         break;
385     }
386 }
387
388 void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask)
389 {
390     Q_ASSERT(this);
391
392     m_changeMask |= changeMask;
393
394     if (m_layer->client())
395         m_layer->client()->notifySyncRequired(m_layer);
396 }
397
398 void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform)
399 {
400     // this is the bulk of the work. understanding what the compositor is trying to achieve,
401     // what graphics-view can do, and trying to find a sane common-grounds
402     if (!m_layer || m_changeMask == NoChanges)
403         goto afterLayerChanges;
404
405     if (m_currentContent.contentType == HTMLContentType && (m_changeMask & ParentChange)) {
406         // the WebCore compositor manages item ownership. We have to make sure
407         // graphics-view doesn't try to snatch that ownership...
408         if (!m_layer->parent() && !parentItem())
409             setParentItem(0);
410         else if (m_layer && m_layer->parent() && m_layer->parent()->nativeLayer() != parentItem())
411             setParentItem(m_layer->parent()->nativeLayer());
412     }
413
414     if (m_changeMask & ChildrenChange) {
415         // we basically do an XOR operation on the list of current children
416         // and the list of wanted children, and remove/add
417         QSet<QGraphicsItem*> newChildren;
418         const Vector<GraphicsLayer*> newChildrenVector = (m_layer->children());
419         newChildren.reserve(newChildrenVector.size());
420
421         for (size_t i = 0; i < newChildrenVector.size(); ++i)
422             newChildren.insert(newChildrenVector[i]->platformLayer());
423
424         const QSet<QGraphicsItem*> currentChildren = childItems().toSet();
425         const QSet<QGraphicsItem*> childrenToAdd = newChildren - currentChildren;
426         const QSet<QGraphicsItem*> childrenToRemove = currentChildren - newChildren;
427
428         for (QSet<QGraphicsItem*>::const_iterator it = childrenToAdd.begin(); it != childrenToAdd.end(); ++it)
429              if (QGraphicsItem* w = *it)
430                 w->setParentItem(this);
431
432         for (QSet<QGraphicsItem*>::const_iterator it = childrenToRemove.begin(); it != childrenToRemove.end(); ++it)
433              if (QGraphicsItem* w = *it)
434                 w->setParentItem(0);
435
436         // children are ordered by z-value, let graphics-view know.
437         for (size_t i = 0; i < newChildrenVector.size(); ++i)
438             if (newChildrenVector[i]->platformLayer())
439                 newChildrenVector[i]->platformLayer()->setZValue(i);
440     }
441
442     if (m_changeMask & MaskLayerChange) {
443         // we can't paint here, because we don't know if the mask layer
444         // itself is ready... we'll have to wait till this layer tries to paint
445         setFlag(ItemClipsChildrenToShape, m_layer->maskLayer() || m_layer->masksToBounds());
446         setGraphicsEffect(0);
447         if (m_layer->maskLayer()) {
448             if (GraphicsLayerQtImpl* mask = qobject_cast<GraphicsLayerQtImpl*>(m_layer->maskLayer()->platformLayer()->toGraphicsObject())) {
449                 mask->m_maskEffect = new MaskEffectQt(this, mask);
450                 mask->setCacheMode(NoCache);
451                 setGraphicsEffect(mask->m_maskEffect.data());
452             }
453         }
454     }
455
456     if ((m_changeMask & PositionChange) && (m_layer->position() != m_state.pos))
457         setPos(m_layer->position().x(), m_layer->position().y());
458
459     if (m_changeMask & SizeChange) {
460         if (m_layer->size() != m_state.size) {
461             prepareGeometryChange();
462             m_size = QSizeF(m_layer->size().width(), m_layer->size().height());
463         }
464     }
465
466     // FIXME: this is a hack, due to a probable QGraphicsScene bug when rapidly modifying the perspective
467     // but without this line we get graphic artifacts
468     if ((m_changeMask & ChildrenTransformChange) && m_state.childrenTransform != m_layer->childrenTransform())
469         scene()->update();
470
471     if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange)) {
472         // due to the differences between the way WebCore handles transforms and the way Qt handles transforms,
473         // all these elements affect the transforms of all the descendants.
474         forceUpdateTransform = true;
475     }
476
477     if (m_changeMask & (ContentChange | DrawsContentChange | MaskLayerChange)) {
478         switch (m_pendingContent.contentType) {
479         case PixmapContentType:
480             update();
481             setFlag(ItemHasNoContents, false);
482
483             break;
484
485         case ColorContentType:
486             // no point in caching a solid-color rectangle
487             setCacheMode(m_layer->maskLayer() ? QGraphicsItem::DeviceCoordinateCache : QGraphicsItem::NoCache);
488             if (m_pendingContent.contentType != m_currentContent.contentType || m_pendingContent.contentsBackgroundColor != m_currentContent.contentsBackgroundColor)
489                 update();
490             m_state.drawsContent = false;
491             setFlag(ItemHasNoContents, false);
492
493             // we only use ItemUsesExtendedStyleOption for HTML content - colors don't gain much from that anyway
494             setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false);
495             break;
496
497         case HTMLContentType:
498             if (m_pendingContent.contentType != m_currentContent.contentType)
499                 update();
500             if (!m_state.drawsContent && m_layer->drawsContent())
501                 update();
502                 if (m_layer->drawsContent() && !m_maskEffect) {
503                     const QGraphicsItem::CacheMode mewCacheMode = isTransformAnimationRunning() ? ItemCoordinateCache : DeviceCoordinateCache;
504
505                     // optimization: QGraphicsItem doesn't always perform this test
506                     if (mewCacheMode != cacheMode())
507                         setCacheMode(mewCacheMode);
508
509                     // HTML content: we want to use exposedRect so we don't use WebCore rendering if we don't have to
510                     setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);
511                 }
512             else
513                 setCacheMode(NoCache);
514
515             setFlag(ItemHasNoContents, !m_layer->drawsContent());
516             break;
517         }
518     }
519
520     if ((m_changeMask & OpacityChange) && m_state.opacity != m_layer->opacity())
521         setOpacity(m_layer->opacity());
522
523     if (m_changeMask & ContentsRectChange) {
524         const QRect rect(m_layer->contentsRect());
525         if (m_state.contentsRect != rect) {
526             m_state.contentsRect = rect;
527             update();
528         }
529     }
530
531     if ((m_changeMask & MasksToBoundsChange)
532         && m_state.masksToBounds != m_layer->masksToBounds()) {
533
534         setFlag(QGraphicsItem::ItemClipsToShape, m_layer->masksToBounds());
535         setFlag(QGraphicsItem::ItemClipsChildrenToShape, m_layer->masksToBounds());
536     }
537
538     if ((m_changeMask & ContentsOpaqueChange) && m_state.contentsOpaque != m_layer->contentsOpaque())
539         prepareGeometryChange();
540
541     if (m_maskEffect)
542         m_maskEffect.data()->update();
543     else if (m_changeMask & DisplayChange)
544         update(m_pendingContent.regionToUpdate.boundingRect());
545
546     if ((m_changeMask & BackgroundColorChange) && (m_pendingContent.backgroundColor != m_currentContent.backgroundColor))
547         update();
548
549     // FIXME: the following flags are currently not handled, as they don't have a clear test or are in low priority
550     // GeometryOrientationChange, ContentsOrientationChange, BackfaceVisibilityChange, ChildrenTransformChange, Preserves3DChange
551
552     m_state.maskLayer = m_layer->maskLayer();
553     m_state.pos = m_layer->position();
554     m_state.anchorPoint = m_layer->anchorPoint();
555     m_state.size = m_layer->size();
556     m_state.transform = m_layer->transform();
557     m_state.geoOrientation = m_layer->geometryOrientation();
558     m_state.contentsOrientation =m_layer->contentsOrientation();
559     m_state.opacity = m_layer->opacity();
560     m_state.contentsRect = m_layer->contentsRect();
561     m_state.preserves3D = m_layer->preserves3D();
562     m_state.masksToBounds = m_layer->masksToBounds();
563     m_state.drawsContent = m_layer->drawsContent();
564     m_state.contentsOpaque = m_layer->contentsOpaque();
565     m_state.backfaceVisibility = m_layer->backfaceVisibility();
566     m_state.childrenTransform = m_layer->childrenTransform();
567     m_currentContent.pixmap = m_pendingContent.pixmap;
568     m_currentContent.contentType = m_pendingContent.contentType;
569     m_currentContent.backgroundColor = m_pendingContent.backgroundColor;
570     m_currentContent.regionToUpdate |= m_pendingContent.regionToUpdate;
571     m_currentContent.contentsBackgroundColor = m_pendingContent.contentsBackgroundColor;
572     m_pendingContent.regionToUpdate = QRegion();
573     m_changeMask = NoChanges;
574
575
576 afterLayerChanges:
577     if (forceUpdateTransform)
578         updateTransform();
579
580     if (!recursive)
581         return;    
582
583     QList<QGraphicsItem*> children = childItems();
584     if (m_state.maskLayer)
585         children.append(m_state.maskLayer->platformLayer());
586
587     for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) {
588         if (QGraphicsItem* item = *it)
589             if (GraphicsLayerQtImpl* layer = qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject()))
590                 layer->flushChanges(true, forceUpdateTransform);
591     }
592 }
593
594 void GraphicsLayerQtImpl::notifyAnimationStarted()
595 {
596     // WebCore notifies javascript when the animation starts
597     // here we're letting it know
598     m_layer->client()->notifyAnimationStarted(m_layer, WTF::currentTime());
599 }
600
601 GraphicsLayerQt::GraphicsLayerQt(GraphicsLayerClient* client)
602     : GraphicsLayer(client)
603     , m_impl(PassOwnPtr<GraphicsLayerQtImpl>(new GraphicsLayerQtImpl(this)))
604 {
605 }
606
607 GraphicsLayerQt::~GraphicsLayerQt()
608 {
609 }
610
611 // this is the hook for WebCore compositor to know that Qt implements compositing with GraphicsLayerQt
612 PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client)
613 {
614     return new GraphicsLayerQt(client);
615 }
616
617 // reimp from GraphicsLayer.h: Qt is top-down
618 GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayer::compositingCoordinatesOrientation()
619 {
620     return CompositingCoordinatesTopDown;
621 }
622
623 // reimp from GraphicsLayer.h: we'll need to update the whole display, and we can't count on the current size because it might change
624 void GraphicsLayerQt::setNeedsDisplay()
625 {
626     m_impl->m_pendingContent.regionToUpdate = QRegion(QRect(QPoint(0, 0), QSize(size().width(), size().height())));
627     m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
628 }
629
630 // reimp from GraphicsLayer.h
631 void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& rect)
632 {
633     m_impl->m_pendingContent.regionToUpdate|= QRectF(rect).toAlignedRect();
634     m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
635 }
636
637 // reimp from GraphicsLayer.h
638 void GraphicsLayerQt::setName(const String& name)
639 {
640     m_impl->setObjectName(name);
641     GraphicsLayer::setName(name);
642 }
643
644 // reimp from GraphicsLayer.h
645 void GraphicsLayerQt::setParent(GraphicsLayer* layer)
646 {
647     m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
648     GraphicsLayer::setParent(layer);
649 }
650
651 // reimp from GraphicsLayer.h
652 bool GraphicsLayerQt::setChildren(const Vector<GraphicsLayer*>& children)
653 {
654     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
655     return GraphicsLayer::setChildren(children);
656 }
657
658 // reimp from GraphicsLayer.h
659 void GraphicsLayerQt::addChild(GraphicsLayer* layer)
660 {
661     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
662     GraphicsLayer::addChild(layer);
663 }
664
665 // reimp from GraphicsLayer.h
666 void GraphicsLayerQt::addChildAtIndex(GraphicsLayer* layer, int index)
667 {
668     GraphicsLayer::addChildAtIndex(layer, index);
669     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
670 }
671
672 // reimp from GraphicsLayer.h
673 void GraphicsLayerQt::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling)
674 {
675      GraphicsLayer::addChildAbove(layer, sibling);
676      m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
677 }
678
679 // reimp from GraphicsLayer.h
680 void GraphicsLayerQt::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling)
681 {
682
683     GraphicsLayer::addChildBelow(layer, sibling);
684     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
685 }
686
687 // reimp from GraphicsLayer.h
688 bool GraphicsLayerQt::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild)
689 {
690     if (GraphicsLayer::replaceChild(oldChild, newChild)) {
691         m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
692         return true;
693     }
694
695     return false;
696 }
697
698 // reimp from GraphicsLayer.h
699 void GraphicsLayerQt::removeFromParent()
700 {
701     if (parent())
702         m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
703     GraphicsLayer::removeFromParent();
704 }
705
706 // reimp from GraphicsLayer.h
707 void GraphicsLayerQt::setMaskLayer(GraphicsLayer* layer)
708 {
709     GraphicsLayer::setMaskLayer(layer);
710     m_impl->notifyChange(GraphicsLayerQtImpl::MaskLayerChange);
711 }
712
713 // reimp from GraphicsLayer.h
714 void GraphicsLayerQt::setPosition(const FloatPoint& p)
715 {
716     if (position() != p)
717        m_impl->notifyChange(GraphicsLayerQtImpl::PositionChange);
718     GraphicsLayer::setPosition(p);
719 }
720
721 // reimp from GraphicsLayer.h
722 void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& p)
723 {
724     if (anchorPoint() != p)
725         m_impl->notifyChange(GraphicsLayerQtImpl::AnchorPointChange);
726     GraphicsLayer::setAnchorPoint(p);
727 }
728
729 // reimp from GraphicsLayer.h
730 void GraphicsLayerQt::setSize(const FloatSize& size)
731 {
732     if (this->size() != size)
733         m_impl->notifyChange(GraphicsLayerQtImpl::SizeChange);
734     GraphicsLayer::setSize(size);
735 }
736
737 // reimp from GraphicsLayer.h
738 void GraphicsLayerQt::setTransform(const TransformationMatrix& t)
739 {
740     if (!m_impl->m_transformAnimationRunning && transform() != t)
741        m_impl->notifyChange(GraphicsLayerQtImpl::TransformChange);
742     GraphicsLayer::setTransform(t);
743 }
744
745 // reimp from GraphicsLayer.h
746 void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& t)
747 {
748     GraphicsLayer::setChildrenTransform(t);
749     m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenTransformChange);
750 }
751
752 // reimp from GraphicsLayer.h
753 void GraphicsLayerQt::setPreserves3D(bool b)
754 {
755     if (b != preserves3D());
756        m_impl->notifyChange(GraphicsLayerQtImpl::Preserves3DChange);
757     GraphicsLayer::setPreserves3D(b);
758 }
759
760 // reimp from GraphicsLayer.h
761 void GraphicsLayerQt::setMasksToBounds(bool b)
762 {
763     GraphicsLayer::setMasksToBounds(b);
764     m_impl->notifyChange(GraphicsLayerQtImpl::MasksToBoundsChange);
765 }
766
767 // reimp from GraphicsLayer.h
768 void GraphicsLayerQt::setDrawsContent(bool b)
769 {
770     m_impl->notifyChange(GraphicsLayerQtImpl::DrawsContentChange);
771     GraphicsLayer::setDrawsContent(b);
772 }
773
774 // reimp from GraphicsLayer.h
775 void GraphicsLayerQt::setBackgroundColor(const Color& c)
776 {
777     m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange);
778     m_impl->m_pendingContent.backgroundColor = c;
779     GraphicsLayer::setBackgroundColor(c);
780 }
781
782 // reimp from GraphicsLayer.h
783 void GraphicsLayerQt::clearBackgroundColor()
784 {
785     m_impl->m_pendingContent.backgroundColor = QColor();
786     m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange);
787     GraphicsLayer::clearBackgroundColor();
788 }
789
790 // reimp from GraphicsLayer.h
791 void GraphicsLayerQt::setContentsOpaque(bool b)
792 {
793     m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOpaqueChange);
794     GraphicsLayer::setContentsOpaque(b);
795 }
796
797 // reimp from GraphicsLayer.h
798 void GraphicsLayerQt::setBackfaceVisibility(bool b)
799 {
800     m_impl->notifyChange(GraphicsLayerQtImpl::BackfaceVisibilityChange);
801     GraphicsLayer::setBackfaceVisibility(b);
802 }
803
804 // reimp from GraphicsLayer.h
805 void GraphicsLayerQt::setOpacity(float o)
806 {
807     if (!m_impl->m_opacityAnimationRunning && opacity() != o)
808        m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange);
809     GraphicsLayer::setOpacity(o);
810 }
811
812 // reimp from GraphicsLayer.h
813 void GraphicsLayerQt::setContentsRect(const IntRect& r)
814 {
815     m_impl->notifyChange(GraphicsLayerQtImpl::ContentsRectChange);
816     GraphicsLayer::setContentsRect(r);
817 }
818
819 // reimp from GraphicsLayer.h
820 void GraphicsLayerQt::setContentsToImage(Image* image)
821 {
822     m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
823     m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::HTMLContentType;
824     GraphicsLayer::setContentsToImage(image);
825     if (image) {
826         QPixmap* pxm = image->nativeImageForCurrentFrame();
827         if (pxm) {
828             m_impl->m_pendingContent.pixmap = *pxm;
829             m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::PixmapContentType;
830             return;
831         }        
832     }
833     m_impl->m_pendingContent.pixmap = QPixmap();
834 }
835
836 // reimp from GraphicsLayer.h
837 void GraphicsLayerQt::setContentsBackgroundColor(const Color& color)
838 {
839     m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
840     m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::ColorContentType;
841     m_impl->m_pendingContent.contentsBackgroundColor = QColor(color);
842     GraphicsLayer::setContentsBackgroundColor(color);
843 }
844
845 // reimp from GraphicsLayer.h
846 void GraphicsLayerQt::setGeometryOrientation(CompositingCoordinatesOrientation orientation)
847 {
848     m_impl->notifyChange(GraphicsLayerQtImpl::GeometryOrientationChange);
849     GraphicsLayer::setGeometryOrientation(orientation);
850 }
851
852 // reimp from GraphicsLayer.h
853 void GraphicsLayerQt::setContentsOrientation(CompositingCoordinatesOrientation orientation)
854 {
855     m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOrientationChange);
856     GraphicsLayer::setContentsOrientation(orientation);
857 }
858
859 // reimp from GraphicsLayer.h
860 void GraphicsLayerQt::distributeOpacity(float o)
861 {
862     m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange);
863     m_impl->m_state.distributeOpacity = true;
864 }
865
866 // reimp from GraphicsLayer.h
867 float GraphicsLayerQt::accumulatedOpacity() const
868 {
869     return m_impl->effectiveOpacity();
870 }
871
872 // reimp from GraphicsLayer.h
873 void GraphicsLayerQt::syncCompositingState()
874 {
875     m_impl->flushChanges();
876     GraphicsLayer::syncCompositingState();
877 }
878
879 // reimp from GraphicsLayer.h
880 NativeLayer GraphicsLayerQt::nativeLayer() const
881 {
882     return m_impl.get();
883 }
884
885 // reimp from GraphicsLayer.h
886 PlatformLayer* GraphicsLayerQt::platformLayer() const
887 {
888     return m_impl.get();
889 }
890
891 // now we start dealing with WebCore animations translated to Qt animations
892
893 template <typename T>
894 struct KeyframeValueQt {
895     TimingFunction timingFunction;
896     T value;
897 };
898
899 // we copy this from the AnimationBase.cpp
900 static inline double solveEpsilon(double duration)
901 {
902     return 1.0 / (200.0 * duration);
903 }
904
905 static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, qreal p2y, double t, double duration)
906 {
907     UnitBezier bezier(p1x, p1y, p2x, p2y);
908     return bezier.solve(t, solveEpsilon(duration));
909 }
910
911 // we want the timing function to be as close as possible to what the web-developer intended, so we're using the same function used by WebCore when compositing is disabled
912 // Using easing-curves would probably work for some of the cases, but wouldn't really buy us anything as we'd have to convert the bezier function back to an easing curve
913 static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, int duration)
914 {
915         if (timingFunction.type() == LinearTimingFunction)
916             return progress;
917         if (timingFunction.type() == CubicBezierTimingFunction) {
918             return solveCubicBezierFunction(timingFunction.x1(),
919                                             timingFunction.y1(),
920                                             timingFunction.x2(),
921                                             timingFunction.y2(),
922                                             double(progress), double(duration) / 1000);
923         }
924         return progress;
925 }
926
927 // helper functions to safely get a value out of WebCore's AnimationValue*
928 static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, TransformOperations& transformOperations)
929 {
930     transformOperations = TransformOperations();
931     if (!animationValue)
932         return;
933
934     if (const TransformOperations* ops = static_cast<const TransformAnimationValue*>(animationValue)->value())
935         transformOperations = *ops;
936 }
937
938 static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, qreal& realValue)
939 {
940     realValue = animationValue ? static_cast<const FloatAnimationValue*>(animationValue)->value() : 0;
941 }
942
943 // we put a bit of the functionality in a base class to allow casting and to save some code size
944 class AnimationQtBase : public QAbstractAnimation {
945 public:
946     AnimationQtBase(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
947         : QAbstractAnimation(0)
948         , m_layer(layer)
949         , m_boxSize(boxSize)
950         , m_duration(anim->duration() * 1000)
951         , m_isAlternate(anim->direction() == Animation::AnimationDirectionAlternate)
952         , m_webkitPropertyID(values.property())
953         , m_keyframesName(name)
954     {
955     }
956
957     virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
958     {
959         QAbstractAnimation::updateState(newState, oldState);
960
961         // for some reason I have do this asynchronously - or the animation won't work
962         if (newState == Running && oldState == Stopped && m_layer.data())
963             m_layer.data()->notifyAnimationStartedAsync();
964     }
965
966     virtual int duration() const { return m_duration; }
967
968     QWeakPointer<GraphicsLayerQtImpl> m_layer;
969     IntSize m_boxSize;
970     int m_duration;
971     bool m_isAlternate;
972     AnimatedPropertyID m_webkitPropertyID;
973     QString m_keyframesName;
974 };
975
976 // we'd rather have a templatized QAbstractAnimation than QPropertyAnimation / QVariantAnimation;
977 // Since we know the types that we're dealing with, the QObject/QProperty/QVariant abstraction
978 // buys us very little in this case, for too much overhead
979 template <typename T>
980 class AnimationQt : public AnimationQtBase {
981
982 public:
983     AnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
984         :AnimationQtBase(layer, values, boxSize, anim, name)
985     {
986         // copying those WebCore structures is not trivial, we have to do it like this
987         for (size_t i = 0; i < values.size(); ++i) {
988             const AnimationValue* animationValue = values.at(i);
989             KeyframeValueQt<T> keyframeValue;
990             if (animationValue->timingFunction())
991                 keyframeValue.timingFunction = *animationValue->timingFunction();
992             webkitAnimationToQtAnimationValue(animationValue, keyframeValue.value);
993             m_keyframeValues[animationValue->keyTime()] = keyframeValue;
994         }
995     }
996
997 protected:
998
999     // this is the part that differs between animated properties
1000     virtual void applyFrame(const T& fromValue, const T& toValue, qreal progress) = 0;
1001
1002     virtual void updateCurrentTime(int currentTime)
1003     {
1004         if (!m_layer)
1005             return;
1006
1007         qreal progress = qreal(currentLoopTime()) / duration();
1008
1009         if (m_isAlternate && currentLoop()%2)
1010             progress = 1-progress;
1011
1012         if (m_keyframeValues.isEmpty())
1013             return;
1014
1015         // we find the current from-to keyframes in our little map
1016         typename QMap<qreal, KeyframeValueQt<T> >::iterator it = m_keyframeValues.find(progress);
1017
1018         // we didn't find an exact match, we try the closest match (lower bound)
1019         if (it == m_keyframeValues.end())
1020             it = m_keyframeValues.lowerBound(progress)-1;
1021
1022         // we didn't find any match - we use the first keyframe
1023         if (it == m_keyframeValues.end())
1024             it = m_keyframeValues.begin();
1025
1026         typename QMap<qreal, KeyframeValueQt<T> >::iterator it2 = it+1;
1027         if (it2 == m_keyframeValues.end())
1028             it2 = m_keyframeValues.begin();
1029         const KeyframeValueQt<T>& fromKeyframe = it.value();
1030         const KeyframeValueQt<T>& toKeyframe = it2.value();
1031
1032         const TimingFunction& timingFunc = fromKeyframe.timingFunction;
1033         const T& fromValue = fromKeyframe.value;
1034         const T& toValue = toKeyframe.value;
1035
1036         // now we have a source keyframe, origin keyframe and a timing function
1037         // we can now process the progress and apply the frame
1038         progress = (!progress || progress == 1 || it.key() == it2.key())
1039                                          ? progress
1040                                          : applyTimingFunction(timingFunc, (progress - it.key()) / (it2.key() - it.key()), duration() / 1000);
1041         applyFrame(fromValue, toValue, progress);
1042     }
1043
1044     QMap<qreal, KeyframeValueQt<T> > m_keyframeValues;
1045 };
1046
1047 class TransformAnimationQt : public AnimationQt<TransformOperations> {
1048 public:
1049     TransformAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
1050         : AnimationQt<TransformOperations>(layer, values, boxSize, anim, name)
1051     {
1052     }
1053
1054     ~TransformAnimationQt()
1055     {
1056         // this came up during the compositing/animation LayoutTests
1057         // when the animation dies, the transform has to go back to default
1058         if (m_layer)
1059             m_layer.data()->updateTransform();
1060     }
1061
1062     // the idea is that we let WebCore manage the transform-operations
1063     // and Qt just manages the animation heartbeat and the bottom-line QTransform
1064     // we get the performance not by using QTransform instead of TransformationMatrix, but by proper caching of
1065     // items that are expensive for WebCore to render. We want the rest to be as close to WebCore's idea as possible.
1066     virtual void applyFrame(const TransformOperations& sourceOperations, const TransformOperations& targetOperations, qreal progress)
1067     {
1068         TransformationMatrix transformMatrix;
1069
1070         // sometimes the animation values from WebCore are misleading and we have to use the actual matrix as source
1071         // The Mac implementation simply doesn't try to accelerate those (e.g. 360deg rotation), but we do.
1072         if (progress == 1 || !targetOperations.size() || sourceOperations == targetOperations) {
1073             TransformationMatrix sourceMatrix;
1074             sourceOperations.apply(m_boxSize, sourceMatrix);
1075             transformMatrix = m_sourceMatrix;
1076             transformMatrix.blend(sourceMatrix, 1 - progress);
1077         } else if (targetOperations.size() != sourceOperations.size()) {
1078             transformMatrix = m_sourceMatrix;
1079             targetOperations.apply(m_boxSize, transformMatrix);
1080             transformMatrix.blend(m_sourceMatrix, progress);
1081         } else {
1082             for (size_t i = 0; i < targetOperations.size(); ++i)
1083                 targetOperations.operations()[i]->blend(sourceOperations.at(i), progress)->apply(transformMatrix, m_boxSize);
1084         }
1085         m_layer.data()->setBaseTransform(transformMatrix);
1086     }
1087
1088     virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1089     {
1090         AnimationQtBase::updateState(newState, oldState);
1091         if (!m_layer)
1092             return;
1093         m_layer.data()->flushChanges(true);
1094
1095         // to increase FPS, we use a less accurate caching mechanism while animation is going on
1096         // this is a UX choice that should probably be customizable
1097         if (newState == QAbstractAnimation::Running) {
1098             m_sourceMatrix = m_layer.data()->m_layer->transform();
1099             m_layer.data()->m_transformAnimationRunning = true;
1100             m_layer.data()->adjustCachingRecursively(true);
1101         } else {
1102             m_layer.data()->m_transformAnimationRunning = false;
1103             m_layer.data()->adjustCachingRecursively(false);
1104         }
1105     }
1106
1107     TransformationMatrix m_sourceMatrix;
1108 };
1109
1110 class OpacityAnimationQt : public AnimationQt<qreal> {
1111 public:
1112     OpacityAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
1113          : AnimationQt<qreal>(layer, values, boxSize, anim, name)
1114     {
1115     }
1116
1117     virtual void applyFrame(const qreal& fromValue, const qreal& toValue, qreal progress)
1118     {
1119         m_layer.data()->setOpacity(qMin<qreal>(qMax<qreal>(fromValue + (toValue-fromValue)*progress, 0), 1));
1120     }
1121
1122     virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
1123     {
1124         QAbstractAnimation::updateState(newState, oldState);
1125
1126         if (m_layer)
1127             m_layer.data()->m_opacityAnimationRunning = (newState == QAbstractAnimation::Running);
1128     }
1129 };
1130
1131 bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset)
1132 {
1133     if (!anim->duration() || !anim->iterationCount())
1134         return false;
1135
1136     QAbstractAnimation* newAnim;
1137
1138     switch (values.property()) {
1139     case AnimatedPropertyOpacity:
1140         newAnim = new OpacityAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName);
1141         break;
1142     case AnimatedPropertyWebkitTransform:
1143         newAnim = new TransformAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName);
1144         break;
1145     default:
1146         return false;
1147     }
1148
1149     // we make sure WebCore::Animation and QAnimation are on the same terms
1150     newAnim->setLoopCount(anim->iterationCount());
1151     m_impl->m_animations.append(QWeakPointer<QAbstractAnimation>(newAnim));
1152     QObject::connect(&m_impl->m_suspendTimer, SIGNAL(timeout()), newAnim, SLOT(resume()));
1153     timeOffset += anim->delay();
1154
1155     // flush now or flicker...
1156     m_impl->flushChanges(false);
1157
1158     if (timeOffset)
1159         QTimer::singleShot(timeOffset * 1000, newAnim, SLOT(start()));
1160     else
1161         newAnim->start();
1162
1163     // we don't need to manage the animation object's lifecycle:
1164     // WebCore would call removeAnimations when it's time to delete.
1165
1166     return true;
1167 }
1168
1169 void GraphicsLayerQt::removeAnimationsForProperty(AnimatedPropertyID id)
1170 {
1171     for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1172         if (*it) {
1173             AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data());
1174             if (anim && anim->m_webkitPropertyID == id) {
1175                 anim->deleteLater();
1176                 it = m_impl->m_animations.erase(it);
1177                 --it;
1178             }
1179         }
1180     }
1181 }
1182
1183 void GraphicsLayerQt::removeAnimationsForKeyframes(const String& name)
1184 {
1185     for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1186         if (*it) {
1187             AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data());
1188             if (anim && anim->m_keyframesName == QString(name)) {
1189                 (*it).data()->deleteLater();
1190                 it = m_impl->m_animations.erase(it);
1191                 --it;
1192             }
1193         }
1194     }
1195 }
1196
1197 void GraphicsLayerQt::pauseAnimation(const String& name, double timeOffset)
1198 {
1199     for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1200         if (!(*it))
1201             continue;
1202
1203         AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data());
1204         if (anim && anim->m_keyframesName == QString(name))
1205             QTimer::singleShot(timeOffset * 1000, anim, SLOT(pause()));
1206     }
1207 }
1208
1209 void GraphicsLayerQt::suspendAnimations(double time)
1210 {
1211     if (m_impl->m_suspendTimer.isActive()) {
1212         m_impl->m_suspendTimer.stop();
1213         m_impl->m_suspendTimer.start(time * 1000);
1214     } else {
1215         for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1216             QAbstractAnimation* anim = it->data();
1217             if (anim)
1218                 anim->pause();
1219         }
1220     }
1221 }
1222
1223 void GraphicsLayerQt::resumeAnimations()
1224 {
1225     if (m_impl->m_suspendTimer.isActive()) {
1226         m_impl->m_suspendTimer.stop();
1227         for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
1228             QAbstractAnimation* anim = (*it).data();
1229             if (anim)
1230                 anim->resume();
1231         }
1232     }
1233 }
1234
1235 }
1236
1237 #include <GraphicsLayerQt.moc>