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