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