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