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