1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_slideshow.hxx"
26 
27 // must be first
28 #include <canvas/debug.hxx>
29 #include <tools/diagnose_ex.h>
30 #include <canvas/verbosetrace.hxx>
31 
32 #include <com/sun/star/animations/AnimationCalcMode.hpp>
33 #include <comphelper/sequence.hxx>
34 
35 #include "activitiesfactory.hxx"
36 #include "smilfunctionparser.hxx"
37 #include "accumulation.hxx"
38 #include "activityparameters.hxx"
39 #include "interpolation.hxx"
40 #include "tools.hxx"
41 #include "simplecontinuousactivitybase.hxx"
42 #include "discreteactivitybase.hxx"
43 #include "continuousactivitybase.hxx"
44 #include "continuouskeytimeactivitybase.hxx"
45 
46 #include <boost/bind.hpp>
47 #include <boost/optional.hpp>
48 
49 #include <cmath> // for modf
50 #include <vector>
51 #include <algorithm>
52 
53 using namespace com::sun::star;
54 
55 namespace slideshow {
56 namespace internal {
57 
58 namespace {
59 
60 /** Traits template, to take formula application only for ValueType = double
61  */
62 template<typename ValueType> struct FormulaTraits
63 {
64     static ValueType getPresentationValue(
65         const ValueType& rVal, const ExpressionNodeSharedPtr& )
66     {
67         return rVal;
68     }
69 };
70 
71 /// Specialization for ValueType = double
72 template<> struct FormulaTraits<double>
73 {
74     static double getPresentationValue(
75         double const& rVal, ExpressionNodeSharedPtr const& rFormula )
76     {
77         return rFormula ? (*rFormula)(rVal) : rVal;
78     }
79 };
80 
81 // Various ActivityBase specializations for different animator types
82 // =================================================================
83 
84 /** FromToBy handler
85 
86     Provides the Activity specializations for FromToBy
87     animations (e.g. those without a values list).
88 
89     This template makes heavy use of SFINAE, only one of
90     the perform*() methods will compile for each of the
91     base classes.
92 
93     Note that we omit the virtual keyword on the perform()
94     overrides on purpose; those that actually do override
95     baseclass virtual methods inherit the property, and
96     the others won't increase our vtable. What's more,
97     having all perform() method in the vtable actually
98     creates POIs for them, which breaks the whole SFINAE
99     concept (IOW, this template won't compile any longer).
100 
101     @tpl BaseType
102     Base class to use for this activity. Only
103     ContinuousActivityBase and DiscreteActivityBase are
104     supported here.
105 
106     @tpl AnimationType
107     Type of the Animation to call.
108 */
109 template<class BaseType, typename AnimationType>
110 class FromToByActivity : public BaseType
111 {
112 public:
113     typedef typename AnimationType::ValueType           ValueType;
114     typedef boost::optional<ValueType>                  OptionalValueType;
115 
116 private:
117     // some compilers don't inline whose definition they haven't
118     // seen before the call site...
119     ValueType getPresentationValue( const ValueType& rVal ) const
120     {
121         return FormulaTraits<ValueType>::getPresentationValue( rVal, mpFormula);
122     }
123 
124 public:
125     /** Create FromToByActivity.
126 
127         @param rFrom
128         From this value, the animation starts
129 
130         @param rTo
131         With this value, the animation ends
132 
133         @param rBy
134         With this value, the animation increments the start value
135 
136         @param rParms
137         Standard Activity parameter struct
138 
139         @param rAnim
140         Shared ptr to AnimationType
141 
142         @param rInterpolator
143         Interpolator object to be used for lerping between
144         start and end value (need to be passed, since it
145         might contain state, e.g. interpolation direction
146         for HSL color space).
147 
148         @param bCumulative
149         Whether repeated animations should cumulate the
150         value, or start fresh each time.
151     */
152     FromToByActivity(
153         const OptionalValueType&                      rFrom,
154         const OptionalValueType&                      rTo,
155         const OptionalValueType&                      rBy,
156         const ActivityParameters&                     rParms,
157         const ::boost::shared_ptr< AnimationType >&   rAnim,
158         const Interpolator< ValueType >&              rInterpolator,
159         bool                                          bCumulative )
160         : BaseType( rParms ),
161           maFrom( rFrom ),
162           maTo( rTo ),
163           maBy( rBy ),
164           mpFormula( rParms.mpFormula ),
165           maStartValue(),
166           maEndValue(),
167           mpAnim( rAnim ),
168           maInterpolator( rInterpolator ),
169           mbDynamicStartValue( false ),
170           mbCumulative( bCumulative )
171     {
172         ENSURE_OR_THROW( mpAnim, "Invalid animation object" );
173 
174         ENSURE_OR_THROW(
175             rTo || rBy,
176             "From and one of To or By, or To or By alone must be valid" );
177     }
178 
179     virtual void startAnimation()
180     {
181         if (this->isDisposed() || !mpAnim)
182             return;
183         BaseType::startAnimation();
184 
185         // start animation
186         mpAnim->start( BaseType::getShape(),
187                        BaseType::getShapeAttributeLayer() );
188 
189         // setup start and end value. Determine animation
190         // start value only when animation actually
191         // started up (this order is part of the Animation
192         // interface contract)
193         const ValueType aAnimationStartValue( mpAnim->getUnderlyingValue() );
194 
195         // first of all, determine general type of
196         // animation, by inspecting which of the FromToBy values
197         // are actually valid.
198         // See http://www.w3.org/TR/smil20/animation.html#AnimationNS-FromToBy
199         // for a definition
200         if( maFrom )
201         {
202             // From-to or From-by animation. According to
203             // SMIL spec, the To value takes precedence
204             // over the By value, if both are specified
205             if( maTo )
206             {
207                 // From-To animation
208                 maStartValue = *maFrom;
209                 maEndValue = *maTo;
210             }
211             else if( maBy )
212             {
213                 // From-By animation
214                 maStartValue = *maFrom;
215                 maEndValue = maStartValue + *maBy;
216             }
217         }
218         else
219         {
220             // By or To animation. According to SMIL spec,
221             // the To value takes precedence over the By
222             // value, if both are specified
223             if( maTo )
224             {
225                 // To animation
226 
227                 // According to the SMIL spec
228                 // (http://www.w3.org/TR/smil20/animation.html#animationNS-ToAnimation),
229                 // the to animation interpolates between
230                 // the _running_ underlying value and the to value (as the end value)
231                 mbDynamicStartValue = true;
232                 maEndValue = *maTo;
233             }
234             else if( maBy )
235             {
236                 // By animation
237                 maStartValue = aAnimationStartValue;
238                 maEndValue = maStartValue + *maBy;
239             }
240         }
241     }
242 
243     virtual void endAnimation()
244     {
245         // end animation
246         if (mpAnim)
247             mpAnim->end();
248     }
249 
250     /// perform override for ContinuousActivityBase
251     void perform( double nModifiedTime, sal_uInt32 nRepeatCount ) const
252     {
253         if (this->isDisposed() || !mpAnim)
254             return;
255         (*mpAnim)(
256             getPresentationValue(
257                 accumulate( maEndValue,
258                             mbCumulative * nRepeatCount, // means: mbCumulative ? nRepeatCount : 0,
259                             maInterpolator( (mbDynamicStartValue
260                                              ? mpAnim->getUnderlyingValue()
261                                              : maStartValue),
262                                             maEndValue,
263                                             nModifiedTime ) ) ) );
264     }
265 
266     using BaseType::perform;
267 
268     /// perform override for DiscreteActivityBase base
269     void perform( sal_uInt32 nFrame, sal_uInt32 nRepeatCount ) const
270     {
271         if (this->isDisposed() || !mpAnim)
272             return;
273         (*mpAnim)(
274             getPresentationValue(
275                 accumulate( maEndValue, mbCumulative ? nRepeatCount : 0,
276                             lerp( maInterpolator,
277                                   (mbDynamicStartValue
278                                    ? mpAnim->getUnderlyingValue()
279                                    : maStartValue),
280                                   maEndValue,
281                                   nFrame,
282                                   BaseType::getNumberOfKeyTimes() ) ) ) );
283     }
284 
285     using BaseType::isAutoReverse;
286 
287     virtual void performEnd()
288     {
289         // xxx todo: good guess
290         if (mpAnim)
291         {
292             if (isAutoReverse())
293                 (*mpAnim)( getPresentationValue( maStartValue ) );
294             else
295                 (*mpAnim)( getPresentationValue( maEndValue ) );
296         }
297     }
298 
299     /// Disposable:
300     virtual void dispose()
301     {
302         mpAnim.reset();
303         BaseType::dispose();
304     }
305 
306 private:
307     const OptionalValueType                 maFrom;
308     const OptionalValueType                 maTo;
309     const OptionalValueType                 maBy;
310 
311     ExpressionNodeSharedPtr                 mpFormula;
312 
313     ValueType                               maStartValue;
314     ValueType                               maEndValue;
315 
316     ::boost::shared_ptr< AnimationType >    mpAnim;
317     Interpolator< ValueType >               maInterpolator;
318     bool                                    mbDynamicStartValue;
319     bool                                    mbCumulative;
320 };
321 
322 
323 /** Generate Activity corresponding to given FromToBy values
324 
325     @tpl BaseType
326     BaseType to use for deriving the Activity from
327 
328     @tpl AnimationType
329     Subtype of the Animation object (e.g. NumberAnimation)
330 */
331 template<class BaseType, typename AnimationType>
332 AnimationActivitySharedPtr createFromToByActivity(
333     const uno::Any&                                          rFromAny,
334     const uno::Any&                                          rToAny,
335     const uno::Any&                                          rByAny,
336     const ActivityParameters&                                rParms,
337     const ::boost::shared_ptr< AnimationType >&              rAnim,
338     const Interpolator< typename AnimationType::ValueType >& rInterpolator,
339     bool                                                     bCumulative,
340     const ShapeSharedPtr&                                    rShape,
341     const ::basegfx::B2DVector&                              rSlideBounds )
342 {
343     typedef typename AnimationType::ValueType           ValueType;
344     typedef boost::optional<ValueType>                  OptionalValueType;
345 
346     OptionalValueType aFrom;
347     OptionalValueType aTo;
348     OptionalValueType aBy;
349 
350     ValueType aTmpValue;
351 
352     if( rFromAny.hasValue() )
353     {
354         ENSURE_OR_THROW(
355             extractValue( aTmpValue, rFromAny, rShape, rSlideBounds ),
356             "createFromToByActivity(): Could not extract from value" );
357         aFrom.reset(aTmpValue);
358     }
359     if( rToAny.hasValue() )
360     {
361         ENSURE_OR_THROW(
362             extractValue( aTmpValue, rToAny, rShape, rSlideBounds ),
363             "createFromToByActivity(): Could not extract to value" );
364         aTo.reset(aTmpValue);
365     }
366     if( rByAny.hasValue() )
367     {
368         ENSURE_OR_THROW(
369             extractValue( aTmpValue, rByAny, rShape, rSlideBounds ),
370             "createFromToByActivity(): Could not extract by value" );
371         aBy.reset(aTmpValue);
372     }
373 
374     return AnimationActivitySharedPtr(
375         new FromToByActivity<BaseType, AnimationType>(
376             aFrom,
377             aTo,
378             aBy,
379             rParms,
380             rAnim,
381             rInterpolator,
382             bCumulative ) );
383 }
384 
385 /* The following table shows which animator combines with
386    which Activity type:
387 
388    NumberAnimator:  all
389    PairAnimation:   all
390    ColorAnimation:  all
391    StringAnimation: DiscreteActivityBase
392    BoolAnimation:   DiscreteActivityBase
393 */
394 
395 /** Values handler
396 
397     Provides the Activity specializations for value lists
398     animations.
399 
400     This template makes heavy use of SFINAE, only one of
401     the perform*() methods will compile for each of the
402     base classes.
403 
404     Note that we omit the virtual keyword on the perform()
405     overrides on purpose; those that actually do override
406     baseclass virtual methods inherit the property, and
407     the others won't increase our vtable. What's more,
408     having all perform() method in the vtable actually
409     creates POIs for them, which breaks the whole SFINAE
410     concept (IOW, this template won't compile any longer).
411 
412     @tpl BaseType
413     Base class to use for this activity. Only
414     ContinuousKeyTimeActivityBase and DiscreteActivityBase
415     are supported here. For values animation without key
416     times, the client must emulate key times by providing
417     a vector of equally spaced values between 0 and 1,
418     with the same number of entries as the values vector.
419 
420     @tpl AnimationType
421     Type of the Animation to call.
422 */
423 template<class BaseType, typename AnimationType>
424 class ValuesActivity : public BaseType
425 {
426 public:
427     typedef typename AnimationType::ValueType   ValueType;
428     typedef std::vector<ValueType>              ValueVectorType;
429 
430 private:
431     // some compilers don't inline methods whose definition they haven't
432     // seen before the call site...
433     ValueType getPresentationValue( const ValueType& rVal ) const
434     {
435         return FormulaTraits<ValueType>::getPresentationValue(
436             rVal, mpFormula );
437     }
438 
439 public:
440     /** Create ValuesActivity.
441 
442         @param rValues
443         Value vector to cycle animation through
444 
445         @param rParms
446         Standard Activity parameter struct
447 
448         @param rAnim
449         Shared ptr to AnimationType
450 
451         @param rInterpolator
452         Interpolator object to be used for lerping between
453         start and end value (need to be passed, since it
454         might contain state, e.g. interpolation direction
455         for HSL color space).
456 
457         @param bCumulative
458         Whether repeated animations should cumulate the
459         value, or start afresh each time.
460     */
461     ValuesActivity(
462         const ValueVectorType&                      rValues,
463         const ActivityParameters&                   rParms,
464         const boost::shared_ptr<AnimationType>&     rAnim,
465         const Interpolator< ValueType >&            rInterpolator,
466         bool                                        bCumulative )
467         : BaseType( rParms ),
468           maValues( rValues ),
469           mpFormula( rParms.mpFormula ),
470           mpAnim( rAnim ),
471           maInterpolator( rInterpolator ),
472           mbCumulative( bCumulative )
473     {
474         ENSURE_OR_THROW( mpAnim, "Invalid animation object" );
475         ENSURE_OR_THROW( !rValues.empty(), "Empty value vector" );
476     }
477 
478     virtual void startAnimation()
479     {
480         if (this->isDisposed() || !mpAnim)
481             return;
482         BaseType::startAnimation();
483 
484         // start animation
485         mpAnim->start( BaseType::getShape(),
486                        BaseType::getShapeAttributeLayer() );
487     }
488 
489     virtual void endAnimation()
490     {
491         // end animation
492         if (mpAnim)
493             mpAnim->end();
494     }
495 
496     /// perform override for ContinuousKeyTimeActivityBase base
497     void perform( sal_uInt32    nIndex,
498                   double        nFractionalIndex,
499                   sal_uInt32    nRepeatCount ) const
500     {
501         if (this->isDisposed() || !mpAnim)
502             return;
503         ENSURE_OR_THROW( nIndex+1 < maValues.size(),
504                           "ValuesActivity::perform(): index out of range" );
505 
506         // interpolate between nIndex and nIndex+1 values
507         (*mpAnim)(
508             getPresentationValue(
509                 accumulate( maValues.back(),
510                             mbCumulative ? nRepeatCount : 0,
511                             maInterpolator( maValues[ nIndex ],
512                                             maValues[ nIndex+1 ],
513                                             nFractionalIndex ) ) ) );
514     }
515 
516     using BaseType::perform;
517 
518     /// perform override for DiscreteActivityBase base
519     void perform( sal_uInt32 nFrame, sal_uInt32 nRepeatCount ) const
520     {
521         if (this->isDisposed() || !mpAnim)
522             return;
523         ENSURE_OR_THROW( nFrame < maValues.size(),
524                           "ValuesActivity::perform(): index out of range" );
525 
526         // this is discrete, thus no lerp here.
527         (*mpAnim)(
528             getPresentationValue(
529                 accumulate( maValues.back(),
530                             mbCumulative ? nRepeatCount : 0,
531                             maValues[ nFrame ] ) ) );
532     }
533 
534     virtual void performEnd()
535     {
536         // xxx todo: good guess
537         if (mpAnim)
538             (*mpAnim)( getPresentationValue( maValues.back() ) );
539     }
540 
541     /// Disposable:
542     virtual void dispose()
543     {
544         mpAnim.reset();
545         BaseType::dispose();
546     }
547 
548 private:
549     ValueVectorType                         maValues;
550 
551     ExpressionNodeSharedPtr                 mpFormula;
552 
553     boost::shared_ptr<AnimationType>        mpAnim;
554     Interpolator< ValueType >               maInterpolator;
555     bool                                    mbCumulative;
556 };
557 
558 /** Generate Activity corresponding to given Value vector
559 
560     @tpl BaseType
561     BaseType to use for deriving the Activity from
562 
563     @tpl AnimationType
564     Subtype of the Animation object (e.g. NumberAnimation)
565 */
566 template<class BaseType, typename AnimationType>
567 AnimationActivitySharedPtr createValueListActivity(
568     const uno::Sequence<uno::Any>&                            rValues,
569     const ActivityParameters&                                 rParms,
570     const boost::shared_ptr<AnimationType>&                   rAnim,
571     const Interpolator<typename AnimationType::ValueType>&    rInterpolator,
572     bool                                                      bCumulative,
573     const ShapeSharedPtr&                                     rShape,
574     const ::basegfx::B2DVector&                               rSlideBounds )
575 {
576     typedef typename AnimationType::ValueType   ValueType;
577     typedef std::vector<ValueType>              ValueVectorType;
578 
579     ValueVectorType aValueVector;
580     aValueVector.reserve( rValues.getLength() );
581 
582     for( ::std::size_t i=0, nLen=rValues.getLength(); i<nLen; ++i )
583     {
584         ValueType aValue;
585         ENSURE_OR_THROW(
586             extractValue( aValue, rValues[i], rShape, rSlideBounds ),
587             "createValueListActivity(): Could not extract values" );
588         aValueVector.push_back( aValue );
589     }
590 
591     return AnimationActivitySharedPtr(
592         new ValuesActivity<BaseType, AnimationType>(
593             aValueVector,
594             rParms,
595             rAnim,
596             rInterpolator,
597             bCumulative ) );
598 }
599 
600 /** Generate Activity for given XAnimate, corresponding to given Value vector
601 
602     @tpl AnimationType
603     Subtype of the Animation object (e.g. NumberAnimation)
604 
605     @param rParms
606     Common activity parameters
607 
608     @param xNode
609     XAnimate node, to retrieve animation values from
610 
611     @param rAnim
612     Actual animation to operate with (gets called with the
613     time-dependent values)
614 
615     @param rInterpolator
616     Interpolator object to be used for lerping between
617     start and end values (need to be passed, since it
618     might contain state, e.g. interpolation direction
619     for HSL color space).
620 */
621 template<typename AnimationType>
622 AnimationActivitySharedPtr createActivity(
623     const ActivitiesFactory::CommonParameters&               rParms,
624     const uno::Reference< animations::XAnimate >&            xNode,
625     const ::boost::shared_ptr< AnimationType >&              rAnim,
626     const Interpolator< typename AnimationType::ValueType >& rInterpolator
627     = Interpolator< typename AnimationType::ValueType >() )
628 {
629     // setup common parameters
630     // =======================
631 
632     ActivityParameters aActivityParms( rParms.mpEndEvent,
633                                        rParms.mrEventQueue,
634                                        rParms.mrActivitiesQueue,
635                                        rParms.mnMinDuration,
636                                        rParms.maRepeats,
637                                        rParms.mnAcceleration,
638                                        rParms.mnDeceleration,
639                                        rParms.mnMinNumberOfFrames,
640                                        rParms.mbAutoReverse );
641 
642     // is a formula given?
643     const ::rtl::OUString& rFormulaString( xNode->getFormula() );
644     if( rFormulaString.getLength() )
645     {
646         // yep, parse and pass to ActivityParameters
647         try
648         {
649             aActivityParms.mpFormula =
650                 SmilFunctionParser::parseSmilFunction(
651                     rFormulaString,
652                     calcRelativeShapeBounds(
653                         rParms.maSlideBounds,
654                         rParms.mpShape->getBounds() ) );
655         }
656         catch( ParseError& )
657         {
658             // parse error, thus no formula
659             OSL_ENSURE( false,
660                         "createActivity(): Error parsing formula string" );
661         }
662     }
663 
664     // are key times given?
665     const uno::Sequence< double >& aKeyTimes( xNode->getKeyTimes() );
666     if( aKeyTimes.hasElements() )
667     {
668         // yes, convert them from Sequence< double >
669         aActivityParms.maDiscreteTimes.resize( aKeyTimes.getLength() );
670         comphelper::sequenceToArray(
671             &aActivityParms.maDiscreteTimes[0],
672             aKeyTimes ); // saves us some temporary vectors
673     }
674 
675     // values sequence given?
676     const sal_Int32 nValueLen( xNode->getValues().getLength() );
677     if( nValueLen )
678     {
679         // Value list activity
680         // ===================
681 
682         // fake keytimes, if necessary
683         if( !aKeyTimes.hasElements() )
684         {
685             // create a dummy vector of key times,
686             // with aValues.getLength equally spaced entries.
687             for( sal_Int32 i=0; i<nValueLen; ++i )
688                 aActivityParms.maDiscreteTimes.push_back( double(i)/nValueLen );
689         }
690 
691         // determine type of animation needed here:
692         // Value list activities are possible with
693         // ContinuousKeyTimeActivityBase and DiscreteActivityBase
694         // specializations
695         const sal_Int16 nCalcMode( xNode->getCalcMode() );
696 
697         switch( nCalcMode )
698         {
699             case animations::AnimationCalcMode::DISCRETE:
700             {
701                 // since DiscreteActivityBase suspends itself
702                 // between the frames, create a WakeupEvent for it.
703                 aActivityParms.mpWakeupEvent.reset(
704                     new WakeupEvent(
705                         rParms.mrEventQueue.getTimer(),
706                         rParms.mrActivitiesQueue ) );
707 
708                 AnimationActivitySharedPtr pActivity(
709                     createValueListActivity< DiscreteActivityBase >(
710                         xNode->getValues(),
711                         aActivityParms,
712                         rAnim,
713                         rInterpolator,
714                         xNode->getAccumulate(),
715                         rParms.mpShape,
716                         rParms.maSlideBounds ) );
717 
718                 // WakeupEvent and DiscreteActivityBase need circular
719                 // references to the corresponding other object.
720                 aActivityParms.mpWakeupEvent->setActivity( pActivity );
721 
722                 return pActivity;
723             }
724 
725             default:
726                 OSL_ENSURE( false, "createActivity(): unexpected case" );
727                 // FALLTHROUGH intended
728             case animations::AnimationCalcMode::PACED:
729                 // FALLTHROUGH intended
730             case animations::AnimationCalcMode::SPLINE:
731                 // FALLTHROUGH intended
732             case animations::AnimationCalcMode::LINEAR:
733                 return createValueListActivity< ContinuousKeyTimeActivityBase >(
734                     xNode->getValues(),
735                     aActivityParms,
736                     rAnim,
737                     rInterpolator,
738                     xNode->getAccumulate(),
739                     rParms.mpShape,
740                     rParms.maSlideBounds );
741         }
742     }
743     else
744     {
745         // FromToBy activity
746         // =================
747 
748         // determine type of animation needed here:
749         // FromToBy activities are possible with
750         // ContinuousActivityBase and DiscreteActivityBase
751         // specializations
752         const sal_Int16 nCalcMode( xNode->getCalcMode() );
753 
754         switch( nCalcMode )
755         {
756             case animations::AnimationCalcMode::DISCRETE:
757             {
758                 // fake keytimes, if necessary
759                 if( !aKeyTimes.hasElements() )
760                 {
761                     // create a dummy vector of 2 key times
762                     const ::std::size_t nLen( 2 );
763                     for( ::std::size_t i=0; i<nLen; ++i )
764                         aActivityParms.maDiscreteTimes.push_back( double(i)/nLen );
765                 }
766 
767                 // since DiscreteActivityBase suspends itself
768                 // between the frames, create a WakeupEvent for it.
769                 aActivityParms.mpWakeupEvent.reset(
770                     new WakeupEvent(
771                         rParms.mrEventQueue.getTimer(),
772                         rParms.mrActivitiesQueue ) );
773 
774                 AnimationActivitySharedPtr pActivity(
775                     createFromToByActivity< DiscreteActivityBase >(
776                         xNode->getFrom(),
777                         xNode->getTo(),
778                         xNode->getBy(),
779                         aActivityParms,
780                         rAnim,
781                         rInterpolator,
782                         xNode->getAccumulate(),
783                         rParms.mpShape,
784                         rParms.maSlideBounds ) );
785 
786                 // WakeupEvent and DiscreteActivityBase need circular
787                 // references to the corresponding other object.
788                 aActivityParms.mpWakeupEvent->setActivity( pActivity );
789 
790                 return pActivity;
791             }
792 
793             default:
794                 OSL_ENSURE( false, "createActivity(): unexpected case" );
795                 // FALLTHROUGH intended
796             case animations::AnimationCalcMode::PACED:
797                 // FALLTHROUGH intended
798             case animations::AnimationCalcMode::SPLINE:
799                 // FALLTHROUGH intended
800             case animations::AnimationCalcMode::LINEAR:
801                 return createFromToByActivity< ContinuousActivityBase >(
802                     xNode->getFrom(),
803                     xNode->getTo(),
804                     xNode->getBy(),
805                     aActivityParms,
806                     rAnim,
807                     rInterpolator,
808                     xNode->getAccumulate(),
809                     rParms.mpShape,
810                     rParms.maSlideBounds );
811         }
812     }
813 }
814 
815 /** Simple activity for ActivitiesFactory::createSimpleActivity
816 
817     @tpl Direction
818     Determines direction of value generator. A 1 yields a
819     forward direction, starting with 0.0 and ending with
820     1.0. A 0 yields a backward direction, starting with
821     1.0 and ending with 0.0
822 */
823 template<int Direction>
824 class SimpleActivity : public ContinuousActivityBase
825 {
826 public:
827     /** Create SimpleActivity.
828 
829         @param rParms
830         Standard Activity parameter struct
831     */
832     SimpleActivity( const ActivityParameters&       rParms,
833                     const NumberAnimationSharedPtr& rAnim ) :
834         ContinuousActivityBase( rParms ),
835         mpAnim( rAnim )
836     {
837         ENSURE_OR_THROW( mpAnim, "Invalid animation object" );
838     }
839 
840     virtual void startAnimation()
841     {
842         if (this->isDisposed() || !mpAnim)
843             return;
844         ContinuousActivityBase::startAnimation();
845 
846         // start animation
847         mpAnim->start( getShape(),
848                        getShapeAttributeLayer() );
849     }
850 
851     virtual void endAnimation()
852     {
853         // end animation
854         if (mpAnim)
855             mpAnim->end();
856     }
857 
858     using SimpleContinuousActivityBase::perform;
859 
860     /// perform override for ContinuousActivityBase
861     virtual void perform( double nModifiedTime, sal_uInt32 ) const
862     {
863         if (this->isDisposed() || !mpAnim)
864             return;
865         // no cumulation, simple [0,1] range
866         (*mpAnim)( 1.0 - Direction + nModifiedTime*(2.0*Direction - 1.0) );
867     }
868 
869     virtual void performEnd()
870     {
871         // xxx todo: review
872         if (mpAnim)
873             (*mpAnim)( 1.0*Direction );
874     }
875 
876     /// Disposable:
877     virtual void dispose()
878     {
879         mpAnim.reset();
880         ContinuousActivityBase::dispose();
881     }
882 
883 private:
884     NumberAnimationSharedPtr    mpAnim;
885 };
886 
887 } // anon namespace
888 
889 
890 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
891     const CommonParameters&                        rParms,
892     const NumberAnimationSharedPtr&                rAnim,
893     const uno::Reference< animations::XAnimate >&  xNode )
894 {
895     // forward to appropriate template instantiation
896     return createActivity( rParms, xNode, rAnim );
897 }
898 
899 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
900     const CommonParameters&                        rParms,
901     const EnumAnimationSharedPtr&                  rAnim,
902     const uno::Reference< animations::XAnimate >&  xNode )
903 {
904     // forward to appropriate template instantiation
905     return createActivity( rParms, xNode, rAnim );
906 }
907 
908 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
909     const CommonParameters&                        rParms,
910     const ColorAnimationSharedPtr&                 rAnim,
911     const uno::Reference< animations::XAnimate >&  xNode )
912 {
913     // forward to appropriate template instantiation
914     return createActivity( rParms, xNode, rAnim );
915 }
916 
917 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
918     const CommonParameters&                            rParms,
919     const HSLColorAnimationSharedPtr&                  rAnim,
920     const uno::Reference< animations::XAnimateColor >& xNode )
921 {
922     // forward to appropriate template instantiation
923     return createActivity( rParms,
924                            uno::Reference< animations::XAnimate >(
925                                xNode, uno::UNO_QUERY_THROW ),
926                            rAnim,
927                            // Direction==true means clockwise in SMIL API
928                            Interpolator< HSLColor >( !xNode->getDirection() ) );
929 }
930 
931 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
932     const CommonParameters&                        rParms,
933     const PairAnimationSharedPtr&                  rAnim,
934     const uno::Reference< animations::XAnimate >&  xNode )
935 {
936     // forward to appropriate template instantiation
937     return createActivity( rParms, xNode, rAnim );
938 }
939 
940 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
941     const CommonParameters&                        rParms,
942     const StringAnimationSharedPtr&                rAnim,
943     const uno::Reference< animations::XAnimate >&  xNode )
944 {
945     // forward to appropriate template instantiation
946     return createActivity( rParms, xNode, rAnim );
947 }
948 
949 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
950     const CommonParameters&                        rParms,
951     const BoolAnimationSharedPtr&                  rAnim,
952     const uno::Reference< animations::XAnimate >&  xNode )
953 {
954     // forward to appropriate template instantiation
955     return createActivity( rParms, xNode, rAnim );
956 }
957 
958 AnimationActivitySharedPtr ActivitiesFactory::createSimpleActivity(
959     const CommonParameters&         rParms,
960     const NumberAnimationSharedPtr& rAnim,
961     bool                            bDirectionForward )
962 {
963     ActivityParameters aActivityParms( rParms.mpEndEvent,
964                                        rParms.mrEventQueue,
965                                        rParms.mrActivitiesQueue,
966                                        rParms.mnMinDuration,
967                                        rParms.maRepeats,
968                                        rParms.mnAcceleration,
969                                        rParms.mnDeceleration,
970                                        rParms.mnMinNumberOfFrames,
971                                        rParms.mbAutoReverse );
972 
973     if( bDirectionForward )
974         return AnimationActivitySharedPtr(
975             new SimpleActivity<1>( aActivityParms, rAnim ) );
976     else
977         return AnimationActivitySharedPtr(
978             new SimpleActivity<0>( aActivityParms, rAnim ) );
979 }
980 
981 } // namespace internal
982 } // namespace presentation
983 
984