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