GraphicsLayerQt.cpp [plain text]
#include "config.h"
#include "GraphicsLayerQt.h"
#include "CurrentTime.h"
#include "FloatRect.h"
#include "GraphicsContext.h"
#include "Image.h"
#include "RefCounted.h"
#include "TranslateTransformOperation.h"
#include "UnitBezier.h"
#include <QtCore/qabstractanimation.h>
#include <QtCore/qdebug.h>
#include <QtCore/qset.h>
#include <QtCore/qtimer.h>
#include <QtGui/qbitmap.h>
#include <QtGui/qcolor.h>
#include <QtGui/qgraphicseffect.h>
#include <QtGui/qgraphicsitem.h>
#include <QtGui/qgraphicsscene.h>
#include <QtGui/qmatrix4x4.h>
#include <QtGui/qpainter.h>
#include <QtGui/qpalette.h>
#include <QtGui/qpixmap.h>
#include <QtGui/qstyleoption.h>
namespace WebCore {
class GraphicsLayerQtImpl : public QGraphicsObject {
Q_OBJECT
public:
enum ChangeMask {
NoChanges = 0,
ChildrenChange = (1L << 1),
MaskLayerChange = (1L << 2),
PositionChange = (1L << 3),
AnchorPointChange = (1L << 4),
SizeChange = (1L << 5),
TransformChange = (1L << 6),
ContentChange = (1L << 7),
GeometryOrientationChange = (1L << 8),
ContentsOrientationChange = (1L << 9),
OpacityChange = (1L << 10),
ContentsRectChange = (1L << 11),
Preserves3DChange = (1L << 12),
MasksToBoundsChange = (1L << 13),
DrawsContentChange = (1L << 14),
ContentsOpaqueChange = (1L << 15),
BackfaceVisibilityChange = (1L << 16),
ChildrenTransformChange = (1L << 17),
DisplayChange = (1L << 18),
BackgroundColorChange = (1L << 19),
ParentChange = (1L << 20),
DistributesOpacityChange = (1L << 21)
};
enum StaticContentType { HTMLContentType, PixmapContentType, ColorContentType};
GraphicsLayerQtImpl(GraphicsLayerQt* newLayer);
virtual ~GraphicsLayerQtImpl();
virtual QPainterPath opaqueArea() const;
virtual QRectF boundingRect() const;
virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
void setBaseTransform(const QTransform&);
void drawContents(QPainter*, const QRectF&, bool mask = false);
void notifyChange(ChangeMask);
void flushChanges(bool recursive = true);
public slots:
void notifyAnimationStarted();
public:
GraphicsLayerQt* m_layer;
QTransform m_baseTransfom;
bool m_transformAnimationRunning;
bool m_opacityAnimationRunning;
struct ContentData {
QPixmap pixmap;
QRegion regionToUpdate;
bool updateAll;
QColor contentsBackgroundColor;
QColor backgroundColor;
StaticContentType contentType;
float opacity;
ContentData()
: updateAll(false)
, contentType(HTMLContentType)
, opacity(1.f)
{
}
};
ContentData m_pendingContent;
ContentData m_currentContent;
int m_changeMask;
QSizeF m_size;
QList<QWeakPointer<QAbstractAnimation> > m_animations;
QTimer m_suspendTimer;
struct State {
GraphicsLayer* maskLayer;
FloatPoint pos;
FloatPoint3D anchorPoint;
FloatSize size;
TransformationMatrix transform;
TransformationMatrix childrenTransform;
Color backgroundColor;
Color currentColor;
GraphicsLayer::CompositingCoordinatesOrientation geoOrientation;
GraphicsLayer::CompositingCoordinatesOrientation contentsOrientation;
float opacity;
QRect contentsRect;
bool preserves3D: 1;
bool masksToBounds: 1;
bool drawsContent: 1;
bool contentsOpaque: 1;
bool backfaceVisibility: 1;
bool distributeOpacity: 1;
bool align: 2;
State(): maskLayer(0), opacity(1), preserves3D(false), masksToBounds(false),
drawsContent(false), contentsOpaque(false), backfaceVisibility(false),
distributeOpacity(false)
{
}
} m_state;
};
GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer)
: QGraphicsObject(0)
, m_layer(newLayer)
, m_transformAnimationRunning(false)
, m_changeMask(NoChanges)
{
setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);
setAcceptedMouseButtons(Qt::NoButton);
setEnabled(false);
setCacheMode(NoCache);
}
GraphicsLayerQtImpl::~GraphicsLayerQtImpl()
{
const QList<QGraphicsItem*> children = childItems();
for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) {
if (QGraphicsItem* item = *it) {
if (scene())
scene()->removeItem(item);
item->setParentItem(0);
}
}
for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_animations.begin(); it != m_animations.end(); ++it)
if (QAbstractAnimation* anim = it->data())
delete anim;
}
void GraphicsLayerQtImpl::setBaseTransform(const QTransform& transform)
{
if (!m_layer)
return;
QPointF originTranslate(
m_layer->anchorPoint().x() * m_layer->size().width(), m_layer->anchorPoint().y() * m_layer->size().height());
resetTransform();
translate(originTranslate.x(), originTranslate.y());
setTransform(transform, true);
translate(-originTranslate.x(), -originTranslate.y());
m_baseTransfom = transform;
}
QPainterPath GraphicsLayerQtImpl::opaqueArea() const
{
QPainterPath painterPath;
if (m_currentContent.backgroundColor.isValid() && m_currentContent.backgroundColor.alpha() == 0xff)
painterPath.addRect(boundingRect());
else {
if (m_state.contentsOpaque
|| (m_currentContent.contentType == ColorContentType && m_currentContent.contentsBackgroundColor.alpha() == 0xff)
|| (m_currentContent.contentType == PixmapContentType && !m_currentContent.pixmap.hasAlpha())) {
painterPath.addRect(m_state.contentsRect);
}
}
return painterPath;
}
QRectF GraphicsLayerQtImpl::boundingRect() const
{
return QRectF(QPointF(0, 0), QSizeF(m_size));
}
void GraphicsLayerQtImpl::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
if (m_state.maskLayer && m_state.maskLayer->platformLayer()) {
GraphicsLayerQtImpl* otherMask = static_cast<GraphicsLayerQtImpl*>(m_state.maskLayer->platformLayer());
otherMask->flushChanges(true);
if (!graphicsEffect()) {
QPixmap mask(QSize(m_state.maskLayer->size().width(), m_state.maskLayer->size().height()));
mask.fill(Qt::transparent);
{
QPainter p(&mask);
p.setRenderHints(painter->renderHints(), true);
p.setCompositionMode(QPainter::CompositionMode_Source);
static_cast<GraphicsLayerQtImpl*>(m_state.maskLayer->platformLayer())->drawContents(&p, option->exposedRect, true);
}
QGraphicsOpacityEffect* opacityEffect = new QGraphicsOpacityEffect(this);
opacityEffect->setOpacity(1);
opacityEffect->setOpacityMask(QBrush(mask));
setGraphicsEffect(opacityEffect);
}
}
drawContents(painter, option->exposedRect);
}
void GraphicsLayerQtImpl::drawContents(QPainter* painter, const QRectF& r, bool mask)
{
QRect rect = r.toAlignedRect();
if (m_currentContent.contentType != HTMLContentType && !m_state.contentsRect.isEmpty())
rect = rect.intersected(m_state.contentsRect);
if (m_currentContent.backgroundColor.isValid())
painter->fillRect(r, QColor(m_currentContent.backgroundColor));
if (!rect.isEmpty()) {
switch (m_currentContent.contentType) {
case PixmapContentType:
painter->drawPixmap(rect.topLeft(), m_currentContent.pixmap.scaled(m_state.contentsRect.size()), r);
break;
case ColorContentType:
painter->fillRect(rect, m_currentContent.contentsBackgroundColor);
break;
default:
if (m_state.drawsContent) {
GraphicsContext gc(painter);
m_layer->paintGraphicsLayerContents(gc, rect);
}
break;
}
}
}
void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask)
{
if (!this)
return;
m_changeMask |= changeMask;
if (m_layer->client())
m_layer->client()->notifySyncRequired(m_layer);
}
void GraphicsLayerQtImpl::flushChanges(bool recursive)
{
if (!m_layer || m_changeMask == NoChanges)
goto afterLayerChanges;
if (m_currentContent.contentType == HTMLContentType && (m_changeMask & ParentChange)) {
if (!m_layer->parent() && !parentItem())
setParentItem(0);
else if (m_layer && m_layer->parent() && m_layer->parent()->nativeLayer() != parentItem())
setParentItem(m_layer->parent()->nativeLayer());
}
if (m_changeMask & ChildrenChange) {
QSet<QGraphicsItem*> newChildren;
const Vector<GraphicsLayer*> newChildrenVector = (m_layer->children());
newChildren.reserve(newChildrenVector.size());
for (size_t i = 0; i < newChildrenVector.size(); ++i)
newChildren.insert(newChildrenVector[i]->platformLayer());
const QSet<QGraphicsItem*> currentChildren = childItems().toSet();
const QSet<QGraphicsItem*> childrenToAdd = newChildren - currentChildren;
const QSet<QGraphicsItem*> childrenToRemove = currentChildren - newChildren;
for (QSet<QGraphicsItem*>::const_iterator it = childrenToAdd.begin(); it != childrenToAdd.end(); ++it) {
if (QGraphicsItem* w = *it)
w->setParentItem(this);
}
for (QSet<QGraphicsItem*>::const_iterator it = childrenToRemove.begin(); it != childrenToRemove.end(); ++it) {
if (QGraphicsItem* w = *it)
w->setParentItem(0);
}
for (size_t i = 0; i < newChildrenVector.size(); ++i)
if (newChildrenVector[i]->platformLayer())
newChildrenVector[i]->platformLayer()->setZValue(i);
}
if (m_changeMask & MaskLayerChange) {
setGraphicsEffect(0);
if (m_layer->maskLayer())
setFlag(ItemClipsChildrenToShape, true);
else
setFlag(ItemClipsChildrenToShape, m_layer->masksToBounds());
update();
}
if ((m_changeMask & PositionChange) && (m_layer->position() != m_state.pos))
setPos(m_layer->position().x(), m_layer->position().y());
if (m_changeMask & SizeChange) {
if (m_layer->size() != m_state.size) {
prepareGeometryChange();
m_size = QSizeF(m_layer->size().width(), m_layer->size().height());
}
}
if (m_changeMask & (TransformChange | AnchorPointChange | SizeChange)) {
if (m_state.transform != m_layer->transform() || m_state.anchorPoint != m_layer->anchorPoint() || m_state.size != m_layer->size())
setBaseTransform(QTransform(m_layer->transform()));
}
if (m_changeMask & (ContentChange | DrawsContentChange)) {
switch (m_pendingContent.contentType) {
case PixmapContentType:
setCacheMode(m_transformAnimationRunning ? ItemCoordinateCache : DeviceCoordinateCache);
update();
setFlag(ItemHasNoContents, false);
break;
case ColorContentType:
setCacheMode(QGraphicsItem::NoCache);
if (m_pendingContent.contentType != m_currentContent.contentType || m_pendingContent.contentsBackgroundColor != m_currentContent.contentsBackgroundColor)
update();
m_state.drawsContent = false;
setFlag(ItemHasNoContents, false);
break;
case HTMLContentType:
if (m_pendingContent.contentType != m_currentContent.contentType)
update();
if (!m_state.drawsContent && m_layer->drawsContent())
update();
if (m_layer->drawsContent())
setCacheMode(m_transformAnimationRunning ? ItemCoordinateCache : DeviceCoordinateCache);
else
setCacheMode(NoCache);
setFlag(ItemHasNoContents, !m_layer->drawsContent());
break;
}
}
if ((m_changeMask & OpacityChange) && m_state.opacity != m_layer->opacity())
setOpacity(m_layer->opacity());
if (m_changeMask & ContentsRectChange) {
const QRect rect(m_layer->contentsRect());
if (m_state.contentsRect != rect) {
m_state.contentsRect = rect;
update();
}
}
if ((m_changeMask & MasksToBoundsChange)
&& m_state.masksToBounds != m_layer->masksToBounds()) {
setFlag(QGraphicsItem::ItemClipsToShape, m_layer->masksToBounds());
setFlag(QGraphicsItem::ItemClipsChildrenToShape, m_layer->masksToBounds());
}
if ((m_changeMask & ContentsOpaqueChange) && m_state.contentsOpaque != m_layer->contentsOpaque())
prepareGeometryChange();
if (m_changeMask & DisplayChange)
update(m_pendingContent.regionToUpdate.boundingRect());
if ((m_changeMask & BackgroundColorChange) && (m_pendingContent.backgroundColor != m_currentContent.backgroundColor))
update();
m_state.maskLayer = m_layer->maskLayer();
m_state.pos = m_layer->position();
m_state.anchorPoint = m_layer->anchorPoint();
m_state.size = m_layer->size();
m_state.transform = m_layer->transform();
m_state.geoOrientation = m_layer->geometryOrientation();
m_state.contentsOrientation =m_layer->contentsOrientation();
m_state.opacity = m_layer->opacity();
m_state.contentsRect = m_layer->contentsRect();
m_state.preserves3D = m_layer->preserves3D();
m_state.masksToBounds = m_layer->masksToBounds();
m_state.drawsContent = m_layer->drawsContent();
m_state.contentsOpaque = m_layer->contentsOpaque();
m_state.backfaceVisibility = m_layer->backfaceVisibility();
m_currentContent.pixmap = m_pendingContent.pixmap;
m_currentContent.contentType = m_pendingContent.contentType;
m_currentContent.backgroundColor = m_pendingContent.backgroundColor;
m_currentContent.regionToUpdate |= m_pendingContent.regionToUpdate;
m_currentContent.contentsBackgroundColor = m_pendingContent.contentsBackgroundColor;
m_pendingContent.regionToUpdate = QRegion();
m_changeMask = NoChanges;
afterLayerChanges:
if (!recursive)
return;
const QList<QGraphicsItem*> children = childItems();
for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) {
if (QGraphicsItem* item = *it)
if (GraphicsLayerQtImpl* layer = qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject()))
layer->flushChanges(true);
}
}
void GraphicsLayerQtImpl::notifyAnimationStarted()
{
m_layer->client()->notifyAnimationStarted(m_layer, WTF::currentTime());
}
GraphicsLayerQt::GraphicsLayerQt(GraphicsLayerClient* client)
: GraphicsLayer(client)
, m_impl(PassOwnPtr<GraphicsLayerQtImpl>(new GraphicsLayerQtImpl(this)))
{
}
GraphicsLayerQt::~GraphicsLayerQt()
{
}
PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client)
{
return new GraphicsLayerQt(client);
}
GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayer::compositingCoordinatesOrientation()
{
return CompositingCoordinatesTopDown;
}
void GraphicsLayerQt::setNeedsDisplay()
{
m_impl->m_pendingContent.regionToUpdate = QRegion(QRect(QPoint(0, 0), QSize(size().width(), size().height())));
m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
}
void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& r)
{
m_impl->m_pendingContent.regionToUpdate|= QRectF(r).toAlignedRect();
m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
}
void GraphicsLayerQt::setName(const String& name)
{
m_impl->setObjectName(name);
GraphicsLayer::setName(name);
}
void GraphicsLayerQt::setParent(GraphicsLayer* layer)
{
m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
GraphicsLayer::setParent(layer);
}
bool GraphicsLayerQt::setChildren(const Vector<GraphicsLayer*>& children)
{
m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
return GraphicsLayer::setChildren(children);
}
void GraphicsLayerQt::addChild(GraphicsLayer* layer)
{
m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
GraphicsLayer::addChild(layer);
}
void GraphicsLayerQt::addChildAtIndex(GraphicsLayer* layer, int index)
{
GraphicsLayer::addChildAtIndex(layer, index);
m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
}
void GraphicsLayerQt::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling)
{
GraphicsLayer::addChildAbove(layer, sibling);
m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
}
void GraphicsLayerQt::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling)
{
GraphicsLayer::addChildBelow(layer, sibling);
m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
}
bool GraphicsLayerQt::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild)
{
if (GraphicsLayer::replaceChild(oldChild, newChild)) {
m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange);
return true;
}
return false;
}
void GraphicsLayerQt::removeFromParent()
{
if (parent())
m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange);
GraphicsLayer::removeFromParent();
}
void GraphicsLayerQt::setMaskLayer(GraphicsLayer* layer)
{
GraphicsLayer::setMaskLayer(layer);
m_impl->notifyChange(GraphicsLayerQtImpl::MaskLayerChange);
}
void GraphicsLayerQt::setPosition(const FloatPoint& p)
{
if (position() != p)
m_impl->notifyChange(GraphicsLayerQtImpl::PositionChange);
GraphicsLayer::setPosition(p);
}
void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& p)
{
if (anchorPoint() != p)
m_impl->notifyChange(GraphicsLayerQtImpl::AnchorPointChange);
GraphicsLayer::setAnchorPoint(p);
}
void GraphicsLayerQt::setSize(const FloatSize& size)
{
if (this->size() != size)
m_impl->notifyChange(GraphicsLayerQtImpl::SizeChange);
GraphicsLayer::setSize(size);
}
void GraphicsLayerQt::setTransform(const TransformationMatrix& t)
{
if (!m_impl->m_transformAnimationRunning && transform() != t)
m_impl->notifyChange(GraphicsLayerQtImpl::TransformChange);
GraphicsLayer::setTransform(t);
}
void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& t)
{
GraphicsLayer::setChildrenTransform(t);
m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenTransformChange);
}
void GraphicsLayerQt::setPreserves3D(bool b)
{
if (b != preserves3D());
m_impl->notifyChange(GraphicsLayerQtImpl::Preserves3DChange);
GraphicsLayer::setPreserves3D(b);
}
void GraphicsLayerQt::setMasksToBounds(bool b)
{
GraphicsLayer::setMasksToBounds(b);
m_impl->notifyChange(GraphicsLayerQtImpl::MasksToBoundsChange);
}
void GraphicsLayerQt::setDrawsContent(bool b)
{
m_impl->notifyChange(GraphicsLayerQtImpl::DrawsContentChange);
GraphicsLayer::setDrawsContent(b);
}
void GraphicsLayerQt::setBackgroundColor(const Color& c)
{
m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange);
m_impl->m_pendingContent.backgroundColor = c;
GraphicsLayer::setBackgroundColor(c);
}
void GraphicsLayerQt::clearBackgroundColor()
{
m_impl->m_pendingContent.backgroundColor = QColor();
m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange);
GraphicsLayer::clearBackgroundColor();
}
void GraphicsLayerQt::setContentsOpaque(bool b)
{
m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOpaqueChange);
GraphicsLayer::setContentsOpaque(b);
}
void GraphicsLayerQt::setBackfaceVisibility(bool b)
{
m_impl->notifyChange(GraphicsLayerQtImpl::BackfaceVisibilityChange);
GraphicsLayer::setBackfaceVisibility(b);
}
void GraphicsLayerQt::setOpacity(float o)
{
if (!m_impl->m_opacityAnimationRunning && opacity() != o)
m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange);
GraphicsLayer::setOpacity(o);
}
void GraphicsLayerQt::setContentsRect(const IntRect& r)
{
m_impl->notifyChange(GraphicsLayerQtImpl::ContentsRectChange);
GraphicsLayer::setContentsRect(r);
}
void GraphicsLayerQt::setContentsToImage(Image* image)
{
m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::HTMLContentType;
GraphicsLayer::setContentsToImage(image);
if (image) {
QPixmap* pxm = image->nativeImageForCurrentFrame();
if (pxm) {
m_impl->m_pendingContent.pixmap = *pxm;
m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::PixmapContentType;
return;
}
}
m_impl->m_pendingContent.pixmap = QPixmap();
}
void GraphicsLayerQt::setContentsBackgroundColor(const Color& color)
{
m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange);
m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::ColorContentType;
m_impl->m_pendingContent.contentsBackgroundColor = QColor(color);
GraphicsLayer::setContentsBackgroundColor(color);
}
void GraphicsLayerQt::setGeometryOrientation(CompositingCoordinatesOrientation orientation)
{
m_impl->notifyChange(GraphicsLayerQtImpl::GeometryOrientationChange);
GraphicsLayer::setGeometryOrientation(orientation);
}
void GraphicsLayerQt::setContentsOrientation(CompositingCoordinatesOrientation orientation)
{
m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOrientationChange);
GraphicsLayer::setContentsOrientation(orientation);
}
void GraphicsLayerQt::distributeOpacity(float o)
{
m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange);
m_impl->m_state.distributeOpacity = true;
}
float GraphicsLayerQt::accumulatedOpacity() const
{
return m_impl->effectiveOpacity();
}
void GraphicsLayerQt::syncCompositingState()
{
m_impl->flushChanges();
GraphicsLayer::syncCompositingState();
}
NativeLayer GraphicsLayerQt::nativeLayer() const
{
return m_impl.get();
}
PlatformLayer* GraphicsLayerQt::platformLayer() const
{
return m_impl.get();
}
template <typename T>
struct KeyframeValueQt {
TimingFunction timingFunction;
T value;
};
static inline double solveEpsilon(double duration)
{
return 1.0 / (200.0 * duration);
}
static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, qreal p2y, double t, double duration)
{
UnitBezier bezier(p1x, p1y, p2x, p2y);
return bezier.solve(t, solveEpsilon(duration));
}
static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, int duration)
{
if (timingFunction.type() == LinearTimingFunction)
return progress;
if (timingFunction.type() == CubicBezierTimingFunction) {
return solveCubicBezierFunction(timingFunction.x1(),
timingFunction.y1(),
timingFunction.x2(),
timingFunction.y2(),
double(progress), double(duration) / 1000);
}
return progress;
}
static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, TransformOperations& transformOperations)
{
transformOperations = TransformOperations();
if (!animationValue)
return;
const TransformOperations* ops = static_cast<const TransformAnimationValue*>(animationValue)->value();
if (ops)
transformOperations = *ops;
}
static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, qreal& realValue)
{
realValue = animationValue ? static_cast<const FloatAnimationValue*>(animationValue)->value() : 0;
}
class AnimationQtBase : public QAbstractAnimation {
public:
AnimationQtBase(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
: QAbstractAnimation(0)
, m_layer(layer)
, m_boxSize(boxSize)
, m_duration(anim->duration() * 1000)
, m_isAlternate(anim->direction() == Animation::AnimationDirectionAlternate)
, m_webkitPropertyID(values.property())
, m_keyframesName(name)
{
}
virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
{
QAbstractAnimation::updateState(newState, oldState);
if (newState == Running && oldState == Stopped)
QTimer::singleShot(0, m_layer.data(), SLOT(notifyAnimationStarted()));
}
virtual int duration() const { return m_duration; }
QWeakPointer<GraphicsLayerQtImpl> m_layer;
IntSize m_boxSize;
int m_duration;
bool m_isAlternate;
AnimatedPropertyID m_webkitPropertyID;
QString m_keyframesName;
};
template <typename T>
class AnimationQt : public AnimationQtBase {
public:
AnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
:AnimationQtBase(layer, values, boxSize, anim, name)
{
for (size_t i = 0; i < values.size(); ++i) {
const AnimationValue* animationValue = values.at(i);
KeyframeValueQt<T> keyframeValue;
if (animationValue->timingFunction())
keyframeValue.timingFunction = *animationValue->timingFunction();
webkitAnimationToQtAnimationValue(animationValue, keyframeValue.value);
m_keyframeValues[animationValue->keyTime()] = keyframeValue;
}
}
protected:
virtual void applyFrame(const T& fromValue, const T& toValue, qreal progress) = 0;
virtual void updateCurrentTime(int currentTime)
{
if (!m_layer)
return;
qreal progress = qreal(currentLoopTime()) / duration();
if (m_isAlternate && currentLoop()%2)
progress = 1-progress;
if (m_keyframeValues.isEmpty())
return;
typename QMap<qreal, KeyframeValueQt<T> >::iterator it = m_keyframeValues.find(progress);
if (it == m_keyframeValues.end())
it = m_keyframeValues.lowerBound(progress)-1;
if (it == m_keyframeValues.end())
it = m_keyframeValues.begin();
typename QMap<qreal, KeyframeValueQt<T> >::iterator it2 = it+1;
if (it2 == m_keyframeValues.end())
it2 = m_keyframeValues.begin();
const KeyframeValueQt<T>& fromKeyframe = it.value();
const KeyframeValueQt<T>& toKeyframe = it2.value();
const TimingFunction& timingFunc = fromKeyframe.timingFunction;
const T& fromValue = fromKeyframe.value;
const T& toValue = toKeyframe.value;
qreal normalizedProgress = (it.key() == it2.key()) ? 0 : (progress - it.key()) / (it2.key() - it.key());
normalizedProgress = applyTimingFunction(timingFunc, normalizedProgress, duration() / 1000);
applyFrame(fromValue, toValue, normalizedProgress);
}
QMap<qreal, KeyframeValueQt<T> > m_keyframeValues;
};
class TransformAnimationQt : public AnimationQt<TransformOperations> {
public:
TransformAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
: AnimationQt<TransformOperations>(layer, values, boxSize, anim, name)
{
}
~TransformAnimationQt()
{
if (m_layer)
m_layer.data()->setBaseTransform(QTransform(m_layer.data()->m_layer->transform()));
}
virtual void applyFrame(const TransformOperations& sourceOperations, const TransformOperations& targetOperations, qreal progress)
{
TransformationMatrix transformMatrix;
for (size_t i = 0; i < targetOperations.size(); ++i)
targetOperations.operations()[i]->blend(sourceOperations.at(i), progress)->apply(transformMatrix, m_boxSize);
m_layer.data()->setBaseTransform(QTransform(transformMatrix));
}
virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
{
AnimationQtBase::updateState(newState, oldState);
if (!m_layer)
return;
m_layer.data()->flushChanges(true);
if (newState == QAbstractAnimation::Running) {
m_layer.data()->m_transformAnimationRunning = true;
if (m_layer.data()->cacheMode() == QGraphicsItem::DeviceCoordinateCache)
m_layer.data()->setCacheMode(QGraphicsItem::ItemCoordinateCache);
} else {
m_layer.data()->m_transformAnimationRunning = false;
if (m_layer.data()->cacheMode() == QGraphicsItem::ItemCoordinateCache)
m_layer.data()->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
}
}
};
class OpacityAnimationQt : public AnimationQt<qreal> {
public:
OpacityAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
: AnimationQt<qreal>(layer, values, boxSize, anim, name)
{
}
virtual void applyFrame(const qreal& fromValue, const qreal& toValue, qreal progress)
{
m_layer.data()->setOpacity(qMin<qreal>(qMax<qreal>(fromValue + (toValue-fromValue)*progress, 0), 1));
}
virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
{
QAbstractAnimation::updateState(newState, oldState);
if (m_layer)
m_layer.data()->m_opacityAnimationRunning = (newState == QAbstractAnimation::Running);
}
};
bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset)
{
if (!anim->duration() || !anim->iterationCount())
return false;
QAbstractAnimation* newAnim;
switch (values.property()) {
case AnimatedPropertyOpacity:
newAnim = new OpacityAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName);
break;
case AnimatedPropertyWebkitTransform:
newAnim = new TransformAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName);
break;
default:
return false;
}
newAnim->setLoopCount(anim->iterationCount());
m_impl->m_animations.append(QWeakPointer<QAbstractAnimation>(newAnim));
QObject::connect(&m_impl->m_suspendTimer, SIGNAL(timeout()), newAnim, SLOT(resume()));
timeOffset += anim->delay();
m_impl->flushChanges(false);
if (timeOffset)
QTimer::singleShot(timeOffset * 1000, newAnim, SLOT(start()));
else
newAnim->start();
QObject::connect(newAnim, SIGNAL(finished()), newAnim, SLOT(deleteLater()));
return true;
}
void GraphicsLayerQt::removeAnimationsForProperty(AnimatedPropertyID id)
{
for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
if (*it) {
AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data());
if (anim && anim->m_webkitPropertyID == id) {
delete anim;
it = m_impl->m_animations.erase(it);
--it;
}
}
}
}
void GraphicsLayerQt::removeAnimationsForKeyframes(const String& name)
{
for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
if (*it) {
AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data());
if (anim && anim->m_keyframesName == QString(name)) {
(*it).data()->deleteLater();
it = m_impl->m_animations.erase(it);
--it;
}
}
}
}
void GraphicsLayerQt::pauseAnimation(const String& name, double timeOffset)
{
for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
if (*it) {
AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data());
if (anim && anim->m_keyframesName == QString(name))
QTimer::singleShot(timeOffset * 1000, anim, SLOT(pause()));
}
}
}
void GraphicsLayerQt::suspendAnimations(double time)
{
if (m_impl->m_suspendTimer.isActive()) {
m_impl->m_suspendTimer.stop();
m_impl->m_suspendTimer.start(time * 1000);
} else {
for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
QAbstractAnimation* anim = it->data();
if (anim)
anim->pause();
}
}
}
void GraphicsLayerQt::resumeAnimations()
{
if (m_impl->m_suspendTimer.isActive()) {
m_impl->m_suspendTimer.stop();
for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
QAbstractAnimation* anim = (*it).data();
if (anim)
anim->resume();
}
}
}
}
#include <GraphicsLayerQt.moc>