LCOV - code coverage report
Current view: top level - slideshow/source/engine/animationnodes - animationbasenode.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 1 122 0.8 %
Date: 2015-06-13 12:38:46 Functions: 2 11 18.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : 
      21             : // must be first
      22             : #include <canvas/debug.hxx>
      23             : #include <canvas/verbosetrace.hxx>
      24             : #include <cppuhelper/exc_hlp.hxx>
      25             : #include <comphelper/anytostring.hxx>
      26             : #include <com/sun/star/presentation/ParagraphTarget.hpp>
      27             : #include <com/sun/star/animations/AnimationNodeType.hpp>
      28             : #include <com/sun/star/animations/Timing.hpp>
      29             : #include <com/sun/star/animations/AnimationAdditiveMode.hpp>
      30             : #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
      31             : 
      32             : #include "nodetools.hxx"
      33             : #include "doctreenode.hxx"
      34             : #include "animationbasenode.hxx"
      35             : #include "delayevent.hxx"
      36             : #include "framerate.hxx"
      37             : 
      38             : #include <boost/bind.hpp>
      39             : #include <boost/optional.hpp>
      40             : #include <algorithm>
      41             : 
      42             : using namespace com::sun::star;
      43             : 
      44             : namespace slideshow {
      45             : namespace internal {
      46             : 
      47           0 : AnimationBaseNode::AnimationBaseNode(
      48             :     const uno::Reference< animations::XAnimationNode >&   xNode,
      49             :     const BaseContainerNodeSharedPtr&                     rParent,
      50             :     const NodeContext&                                    rContext )
      51             :     : BaseNode( xNode, rParent, rContext ),
      52             :       mxAnimateNode( xNode, uno::UNO_QUERY_THROW ),
      53             :       maAttributeLayerHolder(),
      54             :       maSlideSize( rContext.maSlideSize ),
      55             :       mpActivity(),
      56             :       mpShape(),
      57             :       mpShapeSubset(),
      58             :       mpSubsetManager(rContext.maContext.mpSubsettableShapeManager),
      59           0 :       mbIsIndependentSubset( rContext.mbIsIndependentSubset )
      60             : {
      61             :     // extract native node targets
      62             :     // ===========================
      63             : 
      64             :     // plain shape target
      65           0 :     uno::Reference< drawing::XShape > xShape( mxAnimateNode->getTarget(),
      66           0 :                                               uno::UNO_QUERY );
      67             : 
      68             :     // distinguish 5 cases:
      69             : 
      70             :     //  - plain shape target
      71             :     //  (NodeContext.mpMasterShapeSubset full set)
      72             : 
      73             :     //  - parent-generated subset (generate an
      74             :     //  independent subset)
      75             : 
      76             :     //  - parent-generated subset from iteration
      77             :     //  (generate a dependent subset)
      78             : 
      79             :     //  - XShape target at the XAnimatioNode (generate
      80             :     //  a plain shape target)
      81             : 
      82             :     //  - ParagraphTarget target at the XAnimationNode
      83             :     //  (generate an independent shape subset)
      84           0 :     if( rContext.mpMasterShapeSubset )
      85             :     {
      86           0 :         if( rContext.mpMasterShapeSubset->isFullSet() )
      87             :         {
      88             :             // case 1: plain shape target from parent
      89           0 :             mpShape = rContext.mpMasterShapeSubset->getSubsetShape();
      90             :         }
      91             :         else
      92             :         {
      93             :             // cases 2 & 3: subset shape
      94           0 :             mpShapeSubset = rContext.mpMasterShapeSubset;
      95             :         }
      96             :     }
      97             :     else
      98             :     {
      99             :         // no parent-provided shape, try to extract
     100             :         // from XAnimationNode - cases 4 and 5
     101             : 
     102           0 :         if( xShape.is() )
     103             :         {
     104           0 :             mpShape = lookupAttributableShape( getContext().mpSubsettableShapeManager,
     105           0 :                                                xShape );
     106             :         }
     107             :         else
     108             :         {
     109             :             // no shape provided. Maybe a ParagraphTarget?
     110           0 :             presentation::ParagraphTarget aTarget;
     111             : 
     112           0 :             if( !(mxAnimateNode->getTarget() >>= aTarget) )
     113           0 :                 ENSURE_OR_THROW(
     114             :                     false, "could not extract any target information" );
     115             : 
     116           0 :             xShape = aTarget.Shape;
     117             : 
     118           0 :             ENSURE_OR_THROW( xShape.is(), "invalid shape in ParagraphTarget" );
     119             : 
     120           0 :             mpShape = lookupAttributableShape( getContext().mpSubsettableShapeManager,
     121           0 :                                                xShape );
     122             : 
     123             :             // NOTE: For shapes with ParagraphTarget, we ignore
     124             :             // the SubItem property. We implicitly assume that it
     125             :             // is set to ONLY_TEXT.
     126             :             OSL_ENSURE(
     127             :                 mxAnimateNode->getSubItem() ==
     128             :                 presentation::ShapeAnimationSubType::ONLY_TEXT ||
     129             :                 mxAnimateNode->getSubItem() ==
     130             :                 presentation::ShapeAnimationSubType::AS_WHOLE,
     131             :                 "ParagraphTarget given, but subitem not AS_TEXT or AS_WHOLE? "
     132             :                 "Make up your mind, I'll ignore the subitem." );
     133             : 
     134             :             // okay, found a ParagraphTarget with a valid XShape. Does the shape
     135             :             // provide the given paragraph?
     136           0 :             if( aTarget.Paragraph >= 0 &&
     137           0 :                 mpShape->getTreeNodeSupplier().getNumberOfTreeNodes(
     138           0 :                     DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH) > aTarget.Paragraph )
     139             :             {
     140             :                 const DocTreeNode& rTreeNode(
     141           0 :                     mpShape->getTreeNodeSupplier().getTreeNode(
     142             :                         aTarget.Paragraph,
     143           0 :                         DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ) );
     144             : 
     145             :                 // CAUTION: the creation of the subset shape
     146             :                 // _must_ stay in the node constructor, since
     147             :                 // Slide::prefetchShow() initializes shape
     148             :                 // attributes right after animation import (or
     149             :                 // the Slide class must be changed).
     150             :                 mpShapeSubset.reset(
     151             :                     new ShapeSubset( mpShape,
     152             :                                      rTreeNode,
     153           0 :                                      mpSubsetManager ));
     154             : 
     155             :                 // Override NodeContext, and flag this node as
     156             :                 // a special independent subset one. This is
     157             :                 // important when applying initial attributes:
     158             :                 // independent shape subsets must be setup
     159             :                 // when the slide starts, since they, as their
     160             :                 // name suggest, can have state independent to
     161             :                 // the master shape. The following example
     162             :                 // might illustrate that: a master shape has
     163             :                 // no effect, one of the text paragraphs
     164             :                 // within it has an appear effect. Now, the
     165             :                 // respective paragraph must be invisible when
     166             :                 // the slide is initially shown, and become
     167             :                 // visible only when the effect starts.
     168           0 :                 mbIsIndependentSubset = true;
     169             : 
     170             :                 // already enable subset right here, the
     171             :                 // setup of initial shape attributes of
     172             :                 // course needs the subset shape
     173             :                 // generated, to apply e.g. visibility
     174             :                 // changes.
     175           0 :                 mpShapeSubset->enableSubsetShape();
     176           0 :             }
     177             :         }
     178           0 :     }
     179           0 : }
     180             : 
     181           0 : void AnimationBaseNode::dispose()
     182             : {
     183           0 :     if (mpActivity) {
     184           0 :         mpActivity->dispose();
     185           0 :         mpActivity.reset();
     186             :     }
     187             : 
     188           0 :     maAttributeLayerHolder.reset();
     189           0 :     mxAnimateNode.clear();
     190           0 :     mpShape.reset();
     191           0 :     mpShapeSubset.reset();
     192             : 
     193           0 :     BaseNode::dispose();
     194           0 : }
     195             : 
     196           0 : bool AnimationBaseNode::init_st()
     197             : {
     198             :     // if we've still got an old activity lying around, dispose it:
     199           0 :     if (mpActivity) {
     200           0 :         mpActivity->dispose();
     201           0 :         mpActivity.reset();
     202             :     }
     203             : 
     204             :     // note: actually disposing the activity too early might cause problems,
     205             :     // because on dequeued() it calls endAnimation(pAnim->end()), thus ending
     206             :     // animation _after_ last screen update.
     207             :     // review that end() is properly called (which calls endAnimation(), too).
     208             : 
     209             :     try {
     210             :         // TODO(F2): For restart functionality, we must regenerate activities,
     211             :         // since they are not able to reset their state (or implement _that_)
     212           0 :         mpActivity = createActivity();
     213             :     }
     214           0 :     catch (uno::Exception const&) {
     215             :         OSL_FAIL( OUStringToOString(
     216             :                         comphelper::anyToString(cppu::getCaughtException()),
     217             :                         RTL_TEXTENCODING_UTF8).getStr() );
     218             :         // catch and ignore. We later handle empty activities, but for
     219             :         // other nodes to function properly, the core functionality of
     220             :         // this node must remain up and running.
     221             :     }
     222           0 :     return true;
     223             : }
     224             : 
     225           0 : bool AnimationBaseNode::resolve_st()
     226             : {
     227             :     // enable shape subset for automatically generated
     228             :     // subsets. Independent subsets are already setup
     229             :     // during construction time. Doing it only here
     230             :     // saves us a lot of sprites and shapes lying
     231             :     // around. This is especially important for
     232             :     // character-wise iterations, since the shape
     233             :     // content (e.g. thousands of characters) would
     234             :     // otherwise be painted character-by-character.
     235           0 :     if (isDependentSubsettedShape() && mpShapeSubset) {
     236           0 :         mpShapeSubset->enableSubsetShape();
     237             :     }
     238           0 :     return true;
     239             : }
     240             : 
     241           0 : void AnimationBaseNode::activate_st()
     242             : {
     243             :     // create new attribute layer
     244           0 :     maAttributeLayerHolder.createAttributeLayer( getShape() );
     245             : 
     246           0 :     ENSURE_OR_THROW( maAttributeLayerHolder.get(),
     247             :                       "Could not generate shape attribute layer" );
     248             : 
     249             :     // TODO(Q2): This affects the way mpActivity
     250             :     // works, but is performed here because of
     251             :     // locality (we're fiddling with the additive mode
     252             :     // here, anyway, and it's the only place where we
     253             :     // do). OTOH, maybe the complete additive mode
     254             :     // setup should be moved to the activities.
     255             : 
     256             :     // for simple by-animations, the SMIL spec
     257             :     // requires us to emulate "0,by-value" value list
     258             :     // behaviour, with additive mode forced to "sum",
     259             :     // no matter what the input is
     260             :     // (http://www.w3.org/TR/smil20/animation.html#adef-by).
     261           0 :     if( mxAnimateNode->getBy().hasValue() &&
     262           0 :         !mxAnimateNode->getTo().hasValue() &&
     263           0 :         !mxAnimateNode->getFrom().hasValue() )
     264             :     {
     265             :         // force attribute mode to REPLACE (note the
     266             :         // subtle discrepancy to the paragraph above,
     267             :         // where SMIL requires SUM. This is internally
     268             :         // handled by the FromToByActivity, and is
     269             :         // because otherwise DOM values would not be
     270             :         // handled correctly: the activity cannot
     271             :         // determine whether an
     272             :         // Activity::getUnderlyingValue() yields the
     273             :         // DOM value, or already a summed-up conglomerate)
     274             : 
     275             :         // Note that this poses problems with our
     276             :         // hybrid activity duration (time or min number of frames),
     277             :         // since if activities
     278             :         // exceed their duration, wrong 'by' start
     279             :         // values might arise ('Laser effect')
     280             :         maAttributeLayerHolder.get()->setAdditiveMode(
     281           0 :             animations::AnimationAdditiveMode::REPLACE );
     282             :     }
     283             :     else
     284             :     {
     285             :         // apply additive mode to newly created Attribute layer
     286             :         maAttributeLayerHolder.get()->setAdditiveMode(
     287           0 :             mxAnimateNode->getAdditive() );
     288             :     }
     289             : 
     290             :     // fake normal animation behaviour, even if we
     291             :     // show nothing.  This is the appropriate way to
     292             :     // handle errors on Activity generation, because
     293             :     // maybe all other effects on the slide are
     294             :     // correctly initialized (but won't run, if we
     295             :     // signal an error here)
     296           0 :     if (mpActivity) {
     297             :         // supply Activity (and the underlying Animation) with
     298             :         // it's AttributeLayer, to perform the animation on
     299           0 :         mpActivity->setTargets( getShape(), maAttributeLayerHolder.get() );
     300             : 
     301             :         // add to activities queue
     302           0 :         getContext().mrActivitiesQueue.addActivity( mpActivity );
     303             :     }
     304             :     else {
     305             :         // Actually, DO generate the event for empty activity,
     306             :         // to keep the chain of animations running
     307           0 :         BaseNode::scheduleDeactivationEvent();
     308             :     }
     309           0 : }
     310             : 
     311           0 : void AnimationBaseNode::deactivate_st( NodeState eDestState )
     312             : {
     313           0 :     if (eDestState == FROZEN) {
     314           0 :         if (mpActivity)
     315           0 :             mpActivity->end();
     316             :     }
     317             : 
     318           0 :     if (isDependentSubsettedShape()) {
     319             :         // for dependent subsets, remove subset shape
     320             :         // from layer, re-integrate subsetted part
     321             :         // back into original shape. For independent
     322             :         // subsets, we cannot make any assumptions
     323             :         // about subset attribute state relative to
     324             :         // master shape, thus, have to keep it. This
     325             :         // will effectively re-integrate the subsetted
     326             :         // part into the original shape (whose
     327             :         // animation will hopefully have ended, too)
     328             : 
     329             :         // this statement will save a whole lot of
     330             :         // sprites for iterated text effects, since
     331             :         // those sprites will only exist during the
     332             :         // actual lifetime of the effects
     333           0 :         if (mpShapeSubset) {
     334           0 :             mpShapeSubset->disableSubsetShape();
     335             :         }
     336             :     }
     337             : 
     338           0 :     if (eDestState == ENDED) {
     339             : 
     340             :         // no shape anymore, no layer needed:
     341           0 :         maAttributeLayerHolder.reset();
     342             : 
     343           0 :         if (! isDependentSubsettedShape()) {
     344             : 
     345             :             // for all other shapes, removing the
     346             :             // attribute layer quite possibly changes
     347             :             // shape display. Thus, force update
     348           0 :             AttributableShapeSharedPtr const pShape( getShape() );
     349             : 
     350             :             // don't anybody dare to check against
     351             :             // pShape->isVisible() here, removing the
     352             :             // attribute layer might actually make the
     353             :             // shape invisible!
     354           0 :             getContext().mpSubsettableShapeManager->notifyShapeUpdate( pShape );
     355             :         }
     356             : 
     357           0 :         if (mpActivity) {
     358             :             // kill activity, if still running
     359           0 :             mpActivity->dispose();
     360           0 :             mpActivity.reset();
     361             :         }
     362             :     }
     363           0 : }
     364             : 
     365           0 : bool AnimationBaseNode::hasPendingAnimation() const
     366             : {
     367             :     // TODO(F1): This might not always be true. Are there 'inactive'
     368             :     // animation nodes?
     369           0 :     return true;
     370             : }
     371             : 
     372             : #if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
     373             : void AnimationBaseNode::showState() const
     374             : {
     375             :     BaseNode::showState();
     376             : 
     377             :     VERBOSE_TRACE( "AnimationBaseNode info: independent subset=%s",
     378             :                    mbIsIndependentSubset ? "y" : "n" );
     379             : }
     380             : #endif
     381             : 
     382             : ActivitiesFactory::CommonParameters
     383           0 : AnimationBaseNode::fillCommonParameters() const
     384             : {
     385           0 :     double nDuration = 0.0;
     386             : 
     387             :     // TODO(F3): Duration/End handling is barely there
     388           0 :     if( !(mxAnimateNode->getDuration() >>= nDuration) ) {
     389           0 :         mxAnimateNode->getEnd() >>= nDuration; // Wah.
     390             :     }
     391             : 
     392             :     // minimal duration we fallback to (avoid 0 here!)
     393           0 :     nDuration = ::std::max( 0.001, nDuration );
     394             : 
     395           0 :     const bool bAutoReverse( mxAnimateNode->getAutoReverse() );
     396             : 
     397           0 :     boost::optional<double> aRepeats;
     398           0 :     double nRepeats = 0;
     399           0 :     if( (mxAnimateNode->getRepeatCount() >>= nRepeats) ) {
     400           0 :         aRepeats.reset( nRepeats );
     401             :     }
     402             :     else {
     403           0 :         if( (mxAnimateNode->getRepeatDuration() >>= nRepeats) ) {
     404             :             // when repeatDuration is given,
     405             :             // autoreverse does _not_ modify the
     406             :             // active duration. Thus, calc repeat
     407             :             // count with already adapted simple
     408             :             // duration (twice the specified duration)
     409             : 
     410             :             // convert duration back to repeat counts
     411           0 :             if( bAutoReverse )
     412           0 :                 aRepeats.reset( nRepeats / (2.0 * nDuration) );
     413             :             else
     414           0 :                 aRepeats.reset( nRepeats / nDuration );
     415             :         }
     416             :         else
     417             :         {
     418             :             // no double value for both values - Timing::INDEFINITE?
     419             :             animations::Timing eTiming;
     420             : 
     421           0 :             if( !(mxAnimateNode->getRepeatDuration() >>= eTiming) ||
     422           0 :                 eTiming != animations::Timing_INDEFINITE )
     423             :             {
     424           0 :                 if( !(mxAnimateNode->getRepeatCount() >>= eTiming) ||
     425           0 :                     eTiming != animations::Timing_INDEFINITE )
     426             :                 {
     427             :                     // no indefinite timing, no other values given -
     428             :                     // use simple run, i.e. repeat of 1.0
     429           0 :                     aRepeats.reset( 1.0 );
     430             :                 }
     431             :             }
     432             :         }
     433             :     }
     434             : 
     435             :     // calc accel/decel:
     436           0 :     double nAcceleration = 0.0;
     437           0 :     double nDeceleration = 0.0;
     438           0 :     BaseNodeSharedPtr const pSelf( getSelf() );
     439           0 :     for ( boost::shared_ptr<BaseNode> pNode( pSelf );
     440           0 :           pNode; pNode = pNode->getParentNode() )
     441             :     {
     442             :         uno::Reference<animations::XAnimationNode> const xAnimationNode(
     443           0 :             pNode->getXAnimationNode() );
     444             :         nAcceleration = std::max( nAcceleration,
     445           0 :                                   xAnimationNode->getAcceleration() );
     446             :         nDeceleration = std::max( nDeceleration,
     447           0 :                                   xAnimationNode->getDecelerate() );
     448           0 :     }
     449             : 
     450           0 :     EventSharedPtr pEndEvent;
     451           0 :     if (pSelf) {
     452           0 :         pEndEvent = makeEvent(
     453             :             boost::bind( &AnimationNode::deactivate, pSelf ),
     454           0 :             "AnimationBaseNode::deactivate");
     455             :     }
     456             : 
     457             :     // Calculate the minimum frame count that depends on the duration and
     458             :     // the minimum frame count.
     459             :     const sal_Int32 nMinFrameCount (basegfx::clamp<sal_Int32>(
     460           0 :         basegfx::fround(nDuration * FrameRate::MinimumFramesPerSecond), 1, 10));
     461             : 
     462             :     return ActivitiesFactory::CommonParameters(
     463             :         pEndEvent,
     464           0 :         getContext().mrEventQueue,
     465           0 :         getContext().mrActivitiesQueue,
     466             :         nDuration,
     467             :         nMinFrameCount,
     468             :         bAutoReverse,
     469             :         aRepeats,
     470             :         nAcceleration,
     471             :         nDeceleration,
     472             :         getShape(),
     473           0 :         getSlideSize());
     474             : }
     475             : 
     476           0 : AttributableShapeSharedPtr AnimationBaseNode::getShape() const
     477             : {
     478             :     // any subsetting at all?
     479           0 :     if (mpShapeSubset)
     480           0 :         return mpShapeSubset->getSubsetShape();
     481             :     else
     482           0 :         return mpShape; // nope, plain shape always
     483             : }
     484             : 
     485             : } // namespace internal
     486           3 : } // namespace slideshow
     487             : 
     488             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11