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
62namespace WebCore {
63
64static const int gMinimumPixmapCacheLimit = 2048;
65
66#ifndef QT_NO_GRAPHICSEFFECT
67class MaskEffectQt : public QGraphicsEffect {
68public:
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
127class GraphicsLayerQtImpl : public QGraphicsObject
128#if ENABLE(TILED_BACKING_STORE)
129, public virtual TiledBackingStoreClient
130#endif
131{
132    Q_OBJECT
133
134public:
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
215public 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
222signals:
223    // Optimization: Avoid using QTimer::singleShot().
224    void notifyAnimationStartedAsync();
225
226public:
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
318inline GraphicsLayerQtImpl* toGraphicsLayerQtImpl(QGraphicsItem* item)
319{
320    ASSERT(item);
321    return qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject());
322}
323
324inline GraphicsLayerQtImpl* toGraphicsLayerQtImpl(QGraphicsObject* item)
325{
326    return qobject_cast<GraphicsLayerQtImpl*>(item);
327}
328
329GraphicsLayerQtImpl::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
350GraphicsLayerQtImpl::~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
375const GraphicsLayerQtImpl* GraphicsLayerQtImpl::rootLayer() const
376{
377    if (const GraphicsLayerQtImpl* parent = toGraphicsLayerQtImpl(parentObject()))
378        return parent->rootLayer();
379    return this;
380}
381
382
383void 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
391QPixmap 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
520void 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
596void GraphicsLayerQtImpl::setBaseTransform(const TransformationMatrix& baseTransform)
597{
598    m_baseTransform = baseTransform;
599    updateTransform();
600}
601
602QPainterPath 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
620QRectF GraphicsLayerQtImpl::boundingRect() const
621{
622    return QRectF(QPointF(0, 0), QSizeF(m_size));
623}
624
625void 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
664void GraphicsLayerQtImpl::notifySyncRequired()
665{
666    m_blockNotifySyncRequired = false;
667
668    if (m_layer->client())
669        m_layer->client()->notifySyncRequired(m_layer);
670}
671
672void 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
685void 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
869afterLayerChanges:
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*/
892void GraphicsLayerQtImpl::tiledBackingStorePaintBegin()
893{
894}
895
896/* \reimp (TiledBackingStoreClient.h)
897*/
898void GraphicsLayerQtImpl::tiledBackingStorePaint(GraphicsContext* gc,  const IntRect& rect)
899{
900    m_layer->paintGraphicsLayerContents(*gc, rect);
901}
902
903/* \reimp (TiledBackingStoreClient.h)
904*/
905void 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*/
913IntRect GraphicsLayerQtImpl::tiledBackingStoreContentsRect()
914{
915    return m_layer->contentsRect();
916}
917
918/* \reimp (TiledBackingStoreClient.h)
919*/
920Color 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
928IntRect 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
939void 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
945GraphicsLayerQt::GraphicsLayerQt(GraphicsLayerClient* client)
946    : GraphicsLayer(client)
947    , m_impl(PassOwnPtr<GraphicsLayerQtImpl>(new GraphicsLayerQtImpl(this)))
948{
949}
950
951GraphicsLayerQt::~GraphicsLayerQt()
952{
953}
954
955// This is the hook for WebCore compositor to know that Qt implements compositing with GraphicsLayerQt.
956PassOwnPtr<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*/
963void 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*/
971void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& rect)
972{
973    m_impl->m_pendingContent.regionToUpdate |= QRectF(rect).toAlignedRect();
974    m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
975}
976
977void 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*/
993void GraphicsLayerQt::setName(const String& name)
994{
995    m_impl->setObjectName(name);
996    GraphicsLayer::setName(name);
997}
998
999/* \reimp (GraphicsLayer.h)
1000*/
1001void GraphicsLayerQt::setParent(GraphicsLayer* layer)
1002{
1003    m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
1004    GraphicsLayer::setParent(layer);
1005}
1006
1007/* \reimp (GraphicsLayer.h)
1008*/
1009bool 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*/
1017void GraphicsLayerQt::addChild(GraphicsLayer* layer)
1018{
1019    m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
1020    GraphicsLayer::addChild(layer);
1021}
1022
1023/* \reimp (GraphicsLayer.h)
1024*/
1025void 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*/
1033void 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*/
1041void 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*/
1050bool 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*/
1062void GraphicsLayerQt::removeFromParent()
1063{
1064    if (parent())
1065        m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
1066    GraphicsLayer::removeFromParent();
1067}
1068
1069/* \reimp (GraphicsLayer.h)
1070*/
1071void 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*/
1081void 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*/
1091void 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*/
1101void 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*/
1111void 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*/
1121void 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*/
1131void 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*/
1141void 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*/
1151void 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*/
1161void 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*/
1172void 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*/
1183void 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*/
1193void 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*/
1203void 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*/
1213void 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*/
1223void 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*/
1241void 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
1249void 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
1261void GraphicsLayerQt::setContentsToCanvas(PlatformLayer* canvas)
1262{
1263    setContentsToMedia(canvas);
1264}
1265
1266/* \reimp (GraphicsLayer.h)
1267*/
1268void GraphicsLayerQt::setContentsOrientation(CompositingCoordinatesOrientation orientation)
1269{
1270    m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOrientationChange);
1271    GraphicsLayer::setContentsOrientation(orientation);
1272}
1273
1274/* \reimp (GraphicsLayer.h)
1275*/
1276void 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*/
1284float GraphicsLayerQt::accumulatedOpacity() const
1285{
1286    return m_impl->effectiveOpacity();
1287}
1288
1289/* \reimp (GraphicsLayer.h)
1290*/
1291void GraphicsLayerQt::syncCompositingState()
1292{
1293    m_impl->flushChanges();
1294    GraphicsLayer::syncCompositingState();
1295}
1296
1297/* \reimp (GraphicsLayer.h)
1298*/
1299void 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*/
1308PlatformLayer* GraphicsLayerQt::platformLayer() const
1309{
1310    return m_impl.get();
1311}
1312
1313// Now we start dealing with WebCore animations translated to Qt animations
1314
1315template <typename T>
1316struct KeyframeValueQt {
1317    const TimingFunction* timingFunction;
1318    T value;
1319};
1320
1321/* Copied from AnimationBase.cpp
1322*/
1323static inline double solveEpsilon(double duration)
1324{
1325    return 1.0 / (200.0 * duration);
1326}
1327
1328static 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
1334static 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
1341static 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
1365static 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
1375static 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
1382class AnimationQtBase : public QAbstractAnimation {
1383public:
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.
1423template <typename T>
1424class AnimationQt : public AnimationQtBase {
1425
1426public:
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
1443protected:
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
1517class TransformAnimationQt : public AnimationQt<TransformOperations> {
1518public:
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
1588class OpacityAnimationQt : public AnimationQt<qreal> {
1589public:
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
1630bool 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
1684void 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
1702void 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
1720void 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
1736void 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
1750void 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