summaryrefslogtreecommitdiffstats
path: root/slideshow/source/engine/activities
diff options
context:
space:
mode:
Diffstat (limited to 'slideshow/source/engine/activities')
-rw-r--r--slideshow/source/engine/activities/accumulation.hxx84
-rw-r--r--slideshow/source/engine/activities/activitiesfactory.cxx1015
-rw-r--r--slideshow/source/engine/activities/activitybase.cxx234
-rw-r--r--slideshow/source/engine/activities/activitybase.hxx143
-rw-r--r--slideshow/source/engine/activities/activityparameters.hxx134
-rw-r--r--slideshow/source/engine/activities/continuousactivitybase.cxx39
-rw-r--r--slideshow/source/engine/activities/continuousactivitybase.hxx65
-rw-r--r--slideshow/source/engine/activities/continuouskeytimeactivitybase.cxx59
-rw-r--r--slideshow/source/engine/activities/continuouskeytimeactivitybase.hxx74
-rw-r--r--slideshow/source/engine/activities/discreteactivitybase.cxx192
-rw-r--r--slideshow/source/engine/activities/discreteactivitybase.hxx79
-rw-r--r--slideshow/source/engine/activities/interpolation.hxx190
-rw-r--r--slideshow/source/engine/activities/simplecontinuousactivitybase.cxx253
-rw-r--r--slideshow/source/engine/activities/simplecontinuousactivitybase.hxx79
14 files changed, 2640 insertions, 0 deletions
diff --git a/slideshow/source/engine/activities/accumulation.hxx b/slideshow/source/engine/activities/accumulation.hxx
new file mode 100644
index 000000000..ec0419559
--- /dev/null
+++ b/slideshow/source/engine/activities/accumulation.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_ACCUMULATION_HXX
+#define INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_ACCUMULATION_HXX
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+
+namespace slideshow::internal
+ {
+ /** Generic accumulation.
+
+ This template handles value accumulation across repeated
+ effect runs: returned is the end value times the repeat
+ count, plus the current value.
+
+ @param rEndValue
+ End value of the simple animation.
+
+ @param nRepeatCount
+ Number of completed repeats (i.e. 0 during the first
+ effect run)
+
+ @param rCurrValue
+ Current animation value
+ */
+ template< typename ValueType > ValueType accumulate( const ValueType& rEndValue,
+ sal_uInt32 nRepeatCount,
+ const ValueType& rCurrValue )
+ {
+ return nRepeatCount*rEndValue + rCurrValue;
+ }
+
+ /// Specialization for non-addable enums/constant values
+ template<> sal_Int16 accumulate< sal_Int16 >( const sal_Int16&,
+ sal_uInt32,
+ const sal_Int16& rCurrValue )
+ {
+ // always return rCurrValue, it's forbidden to add enums/constant values...
+ return rCurrValue;
+ }
+
+ /// Specialization for non-addable strings
+ template<> OUString accumulate< OUString >( const OUString&,
+ sal_uInt32,
+ const OUString& rCurrValue )
+ {
+ // always return rCurrValue, it's impossible to add strings...
+ return rCurrValue;
+ }
+
+ /// Specialization for non-addable bools
+ template<> bool accumulate< bool >( const bool&,
+ sal_uInt32,
+ const bool& bCurrValue )
+ {
+ // always return bCurrValue, SMIL spec requires to ignore
+ // cumulative behaviour for bools.
+ return bCurrValue;
+ }
+
+}
+
+#endif // INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_ACCUMULATION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/activitiesfactory.cxx b/slideshow/source/engine/activities/activitiesfactory.cxx
new file mode 100644
index 000000000..2dadfea49
--- /dev/null
+++ b/slideshow/source/engine/activities/activitiesfactory.cxx
@@ -0,0 +1,1015 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/animations/AnimationCalcMode.hpp>
+#include <comphelper/sequence.hxx>
+
+#include <activitiesfactory.hxx>
+#include <slideshowexceptions.hxx>
+#include <smilfunctionparser.hxx>
+#include "accumulation.hxx"
+#include "activityparameters.hxx"
+#include "interpolation.hxx"
+#include <tools.hxx>
+#include "simplecontinuousactivitybase.hxx"
+#include "discreteactivitybase.hxx"
+#include "continuousactivitybase.hxx"
+#include "continuouskeytimeactivitybase.hxx"
+
+#include <optional>
+
+#include <memory>
+#include <vector>
+#include <algorithm>
+
+using namespace com::sun::star;
+
+namespace slideshow::internal {
+
+namespace {
+
+/** Traits template, to take formula application only for ValueType = double
+ */
+template<typename ValueType> struct FormulaTraits
+{
+ static ValueType getPresentationValue(
+ const ValueType& rVal, const std::shared_ptr<ExpressionNode>& )
+ {
+ return rVal;
+ }
+};
+
+/// Specialization for ValueType = double
+template<> struct FormulaTraits<double>
+{
+ static double getPresentationValue(
+ double const& rVal, std::shared_ptr<ExpressionNode> const& rFormula )
+ {
+ return rFormula ? (*rFormula)(rVal) : rVal;
+ }
+};
+
+// Various ActivityBase specializations for different animator types
+// =================================================================
+
+/** FromToBy handler
+
+ Provides the Activity specializations for FromToBy
+ animations (e.g. those without a values list).
+
+ This template makes heavy use of SFINAE, only one of
+ the perform*() methods will compile for each of the
+ base classes.
+
+ Note that we omit the virtual keyword on the perform()
+ overrides on purpose; those that actually do override
+ baseclass virtual methods inherit the property, and
+ the others won't increase our vtable. What's more,
+ having all perform() method in the vtable actually
+ creates POIs for them, which breaks the whole SFINAE
+ concept (IOW, this template won't compile any longer).
+
+ @tpl BaseType
+ Base class to use for this activity. Only
+ ContinuousActivityBase and DiscreteActivityBase are
+ supported here.
+
+ @tpl AnimationType
+ Type of the Animation to call.
+*/
+template<class BaseType, typename AnimationType>
+class FromToByActivity : public BaseType
+{
+public:
+ typedef typename AnimationType::ValueType ValueType;
+ typedef std::optional<ValueType> OptionalValueType;
+
+private:
+ // some compilers don't inline whose definition they haven't
+ // seen before the call site...
+ ValueType getPresentationValue( const ValueType& rVal ) const
+ {
+ return FormulaTraits<ValueType>::getPresentationValue( rVal, mpFormula);
+ }
+
+public:
+ /** Create FromToByActivity.
+
+ @param rFrom
+ From this value, the animation starts
+
+ @param rTo
+ With this value, the animation ends
+
+ @param rBy
+ With this value, the animation increments the start value
+
+ @param rParms
+ Standard Activity parameter struct
+
+ @param rAnim
+ Shared ptr to AnimationType
+
+ @param rInterpolator
+ Interpolator object to be used for lerping between
+ start and end value (need to be passed, since it
+ might contain state, e.g. interpolation direction
+ for HSL color space).
+
+ @param bCumulative
+ Whether repeated animations should cumulate the
+ value, or start fresh each time.
+ */
+ FromToByActivity(
+ const OptionalValueType& rFrom,
+ const OptionalValueType& rTo,
+ const OptionalValueType& rBy,
+ const ActivityParameters& rParms,
+ const ::std::shared_ptr< AnimationType >& rAnim,
+ const Interpolator< ValueType >& rInterpolator,
+ bool bCumulative )
+ : BaseType( rParms ),
+ maFrom( rFrom ),
+ maTo( rTo ),
+ maBy( rBy ),
+ mpFormula( rParms.mpFormula ),
+ maStartValue(),
+ maEndValue(),
+ maPreviousValue(),
+ maStartInterpolationValue(),
+ mnIteration( 0 ),
+ mpAnim( rAnim ),
+ maInterpolator( rInterpolator ),
+ mbDynamicStartValue( false ),
+ mbCumulative( bCumulative )
+ {
+ ENSURE_OR_THROW( mpAnim, "Invalid animation object" );
+
+ ENSURE_OR_THROW(
+ rTo || rBy,
+ "From and one of To or By, or To or By alone must be valid" );
+ }
+
+ virtual void startAnimation()
+ {
+ if (this->isDisposed() || !mpAnim)
+ return;
+ BaseType::startAnimation();
+
+ // start animation
+ mpAnim->start( BaseType::getShape(),
+ BaseType::getShapeAttributeLayer() );
+
+ // setup start and end value. Determine animation
+ // start value only when animation actually
+ // started up (this order is part of the Animation
+ // interface contract)
+ const ValueType aAnimationStartValue( mpAnim->getUnderlyingValue() );
+
+ // first of all, determine general type of
+ // animation, by inspecting which of the FromToBy values
+ // are actually valid.
+ // See http://www.w3.org/TR/smil20/animation.html#AnimationNS-FromToBy
+ // for a definition
+ if( maFrom )
+ {
+ // From-to or From-by animation. According to
+ // SMIL spec, the To value takes precedence
+ // over the By value, if both are specified
+ if( maTo )
+ {
+ // From-To animation
+ maStartValue = *maFrom;
+ maEndValue = *maTo;
+ }
+ else if( maBy )
+ {
+ // From-By animation
+ maStartValue = *maFrom;
+ maEndValue = maStartValue + *maBy;
+ }
+ maStartInterpolationValue = maStartValue;
+ }
+ else
+ {
+ maStartValue = aAnimationStartValue;
+ maStartInterpolationValue = maStartValue;
+
+ // By or To animation. According to SMIL spec,
+ // the To value takes precedence over the By
+ // value, if both are specified
+ if( maTo )
+ {
+ // To animation
+
+ // According to the SMIL spec
+ // (http://www.w3.org/TR/smil20/animation.html#animationNS-ToAnimation),
+ // the to animation interpolates between
+ // the _running_ underlying value and the to value (as the end value)
+ mbDynamicStartValue = true;
+ maPreviousValue = maStartValue;
+ maEndValue = *maTo;
+ }
+ else if( maBy )
+ {
+ // By animation
+ maStartValue = aAnimationStartValue;
+ maEndValue = maStartValue + *maBy;
+ }
+ }
+ }
+
+ virtual void endAnimation()
+ {
+ // end animation
+ if (mpAnim)
+ mpAnim->end();
+ }
+
+ /// perform override for ContinuousActivityBase
+ void perform( double nModifiedTime, sal_uInt32 nRepeatCount ) const
+ {
+ if (this->isDisposed() || !mpAnim)
+ return;
+
+ // According to SMIL 3.0 spec 'to' animation if no other (lower priority)
+ // animations are active or frozen then a simple interpolation is performed.
+ // That is, the start interpolation value is constant while the animation
+ // is running, and is equal to the underlying value retrieved when
+ // the animation start.
+ // However if another animation is manipulating the underlying value,
+ // the 'to' animation will initially add to the effect of the lower priority
+ // animation, and increasingly dominate it as it nears the end of the
+ // simple duration, eventually overriding it completely.
+ // That is, each time the underlying value is changed between two
+ // computations of the animation function the new underlying value is used
+ // as start value for the interpolation.
+ // See:
+ // http://www.w3.org/TR/SMIL3/smil-animation.html#animationNS-ToAnimation
+ // (Figure 6 - Effect of Additive to animation example)
+ // Moreover when a 'to' animation is repeated, at each new iteration
+ // the start interpolation value is reset to the underlying value
+ // of the animated property when the animation started,
+ // as it is shown in the example provided by the SMIL 3.0 spec.
+ // This is exactly as Firefox performs SVG 'to' animations.
+ if( mbDynamicStartValue )
+ {
+ if( mnIteration != nRepeatCount )
+ {
+ mnIteration = nRepeatCount;
+ maStartInterpolationValue = maStartValue;
+ }
+ else
+ {
+ ValueType aActualValue = mpAnim->getUnderlyingValue();
+ if( aActualValue != maPreviousValue )
+ maStartInterpolationValue = aActualValue;
+ }
+ }
+
+ ValueType aValue = maInterpolator( maStartInterpolationValue,
+ maEndValue, nModifiedTime );
+
+ // According to the SMIL spec:
+ // Because 'to' animation is defined in terms of absolute values of
+ // the target attribute, cumulative animation is not defined.
+ if( mbCumulative && !mbDynamicStartValue )
+ {
+ // aValue = this.aEndValue * nRepeatCount + aValue;
+ aValue = accumulate( maEndValue, nRepeatCount, aValue );
+ }
+
+ (*mpAnim)( getPresentationValue( aValue ) );
+
+ if( mbDynamicStartValue )
+ {
+ maPreviousValue = mpAnim->getUnderlyingValue();
+ }
+
+ }
+
+ using BaseType::perform;
+
+ /// perform override for DiscreteActivityBase base
+ void perform( sal_uInt32 nFrame, sal_uInt32 nRepeatCount ) const
+ {
+ if (this->isDisposed() || !mpAnim)
+ return;
+ (*mpAnim)(
+ getPresentationValue(
+ accumulate( maEndValue, mbCumulative ? nRepeatCount : 0,
+ lerp( maInterpolator,
+ (mbDynamicStartValue
+ ? mpAnim->getUnderlyingValue()
+ : maStartValue),
+ maEndValue,
+ nFrame,
+ BaseType::getNumberOfKeyTimes() ) ) ) );
+ }
+
+ using BaseType::isAutoReverse;
+
+ virtual void performEnd()
+ {
+ // xxx todo: good guess
+ if (mpAnim)
+ {
+ if (isAutoReverse())
+ (*mpAnim)( getPresentationValue( maStartValue ) );
+ else
+ (*mpAnim)( getPresentationValue( maEndValue ) );
+ }
+ }
+
+ /// Disposable:
+ virtual void dispose()
+ {
+ mpAnim.reset();
+ BaseType::dispose();
+ }
+
+private:
+ const OptionalValueType maFrom;
+ const OptionalValueType maTo;
+ const OptionalValueType maBy;
+
+ std::shared_ptr<ExpressionNode> mpFormula;
+
+ ValueType maStartValue;
+ ValueType maEndValue;
+
+ mutable ValueType maPreviousValue;
+ mutable ValueType maStartInterpolationValue;
+ mutable sal_uInt32 mnIteration;
+
+ ::std::shared_ptr< AnimationType > mpAnim;
+ Interpolator< ValueType > maInterpolator;
+ bool mbDynamicStartValue;
+ bool mbCumulative;
+};
+
+
+/** Generate Activity corresponding to given FromToBy values
+
+ @tpl BaseType
+ BaseType to use for deriving the Activity from
+
+ @tpl AnimationType
+ Subtype of the Animation object (e.g. NumberAnimation)
+*/
+template<class BaseType, typename AnimationType>
+AnimationActivitySharedPtr createFromToByActivity(
+ const uno::Any& rFromAny,
+ const uno::Any& rToAny,
+ const uno::Any& rByAny,
+ const ActivityParameters& rParms,
+ const ::std::shared_ptr< AnimationType >& rAnim,
+ const Interpolator< typename AnimationType::ValueType >& rInterpolator,
+ bool bCumulative,
+ const ShapeSharedPtr& rShape,
+ const ::basegfx::B2DVector& rSlideBounds )
+{
+ typedef typename AnimationType::ValueType ValueType;
+ typedef std::optional<ValueType> OptionalValueType;
+
+ OptionalValueType aFrom;
+ OptionalValueType aTo;
+ OptionalValueType aBy;
+
+ ValueType aTmpValue;
+
+ if( rFromAny.hasValue() )
+ {
+ ENSURE_OR_THROW(
+ extractValue( aTmpValue, rFromAny, rShape, rSlideBounds ),
+ "createFromToByActivity(): Could not extract from value" );
+ aFrom = aTmpValue;
+ }
+ if( rToAny.hasValue() )
+ {
+ ENSURE_OR_THROW(
+ extractValue( aTmpValue, rToAny, rShape, rSlideBounds ),
+ "createFromToByActivity(): Could not extract to value" );
+ aTo = aTmpValue;
+ }
+ if( rByAny.hasValue() )
+ {
+ ENSURE_OR_THROW(
+ extractValue( aTmpValue, rByAny, rShape, rSlideBounds ),
+ "createFromToByActivity(): Could not extract by value" );
+ aBy = aTmpValue;
+ }
+
+ return std::make_shared<FromToByActivity<BaseType, AnimationType>>(
+ aFrom,
+ aTo,
+ aBy,
+ rParms,
+ rAnim,
+ rInterpolator,
+ bCumulative );
+}
+
+/* The following table shows which animator combines with
+ which Activity type:
+
+ NumberAnimator: all
+ PairAnimation: all
+ ColorAnimation: all
+ StringAnimation: DiscreteActivityBase
+ BoolAnimation: DiscreteActivityBase
+*/
+
+/** Values handler
+
+ Provides the Activity specializations for value lists
+ animations.
+
+ This template makes heavy use of SFINAE, only one of
+ the perform*() methods will compile for each of the
+ base classes.
+
+ Note that we omit the virtual keyword on the perform()
+ overrides on purpose; those that actually do override
+ baseclass virtual methods inherit the property, and
+ the others won't increase our vtable. What's more,
+ having all perform() method in the vtable actually
+ creates POIs for them, which breaks the whole SFINAE
+ concept (IOW, this template won't compile any longer).
+
+ @tpl BaseType
+ Base class to use for this activity. Only
+ ContinuousKeyTimeActivityBase and DiscreteActivityBase
+ are supported here. For values animation without key
+ times, the client must emulate key times by providing
+ a vector of equally spaced values between 0 and 1,
+ with the same number of entries as the values vector.
+
+ @tpl AnimationType
+ Type of the Animation to call.
+*/
+template<class BaseType, typename AnimationType>
+class ValuesActivity : public BaseType
+{
+public:
+ typedef typename AnimationType::ValueType ValueType;
+ typedef std::vector<ValueType> ValueVectorType;
+
+private:
+ // some compilers don't inline methods whose definition they haven't
+ // seen before the call site...
+ ValueType getPresentationValue( const ValueType& rVal ) const
+ {
+ return FormulaTraits<ValueType>::getPresentationValue(
+ rVal, mpFormula );
+ }
+
+public:
+ /** Create ValuesActivity.
+
+ @param rValues
+ Value vector to cycle animation through
+
+ @param rParms
+ Standard Activity parameter struct
+
+ @param rAnim
+ Shared ptr to AnimationType
+
+ @param rInterpolator
+ Interpolator object to be used for lerping between
+ start and end value (need to be passed, since it
+ might contain state, e.g. interpolation direction
+ for HSL color space).
+
+ @param bCumulative
+ Whether repeated animations should cumulate the
+ value, or start afresh each time.
+ */
+ ValuesActivity(
+ const ValueVectorType& rValues,
+ const ActivityParameters& rParms,
+ const std::shared_ptr<AnimationType>& rAnim,
+ const Interpolator< ValueType >& rInterpolator,
+ bool bCumulative )
+ : BaseType( rParms ),
+ maValues( rValues ),
+ mpFormula( rParms.mpFormula ),
+ mpAnim( rAnim ),
+ maInterpolator( rInterpolator ),
+ mbCumulative( bCumulative )
+ {
+ ENSURE_OR_THROW( mpAnim, "Invalid animation object" );
+ ENSURE_OR_THROW( !rValues.empty(), "Empty value vector" );
+ }
+
+ virtual void startAnimation()
+ {
+ if (this->isDisposed() || !mpAnim)
+ return;
+ BaseType::startAnimation();
+
+ // start animation
+ mpAnim->start( BaseType::getShape(),
+ BaseType::getShapeAttributeLayer() );
+ }
+
+ virtual void endAnimation()
+ {
+ // end animation
+ if (mpAnim)
+ mpAnim->end();
+ }
+
+ /// perform override for ContinuousKeyTimeActivityBase base
+ void perform( sal_uInt32 nIndex,
+ double nFractionalIndex,
+ sal_uInt32 nRepeatCount ) const
+ {
+ if (this->isDisposed() || !mpAnim)
+ return;
+ ENSURE_OR_THROW( nIndex+1 < maValues.size(),
+ "ValuesActivity::perform(): index out of range" );
+
+ // interpolate between nIndex and nIndex+1 values
+ (*mpAnim)(
+ getPresentationValue(
+ accumulate<ValueType>( maValues.back(),
+ mbCumulative ? nRepeatCount : 0,
+ maInterpolator( maValues[ nIndex ],
+ maValues[ nIndex+1 ],
+ nFractionalIndex ) ) ) );
+ }
+
+ using BaseType::perform;
+
+ /// perform override for DiscreteActivityBase base
+ void perform( sal_uInt32 nFrame, sal_uInt32 nRepeatCount ) const
+ {
+ if (this->isDisposed() || !mpAnim)
+ return;
+ ENSURE_OR_THROW( nFrame < maValues.size(),
+ "ValuesActivity::perform(): index out of range" );
+
+ // this is discrete, thus no lerp here.
+ (*mpAnim)(
+ getPresentationValue(
+ slideshow::internal::accumulate<ValueType>( maValues.back(),
+ mbCumulative ? nRepeatCount : 0,
+ maValues[ nFrame ] ) ) );
+ }
+
+ virtual void performEnd()
+ {
+ // xxx todo: good guess
+ if (mpAnim)
+ (*mpAnim)( getPresentationValue( maValues.back() ) );
+ }
+
+private:
+ ValueVectorType maValues;
+
+ std::shared_ptr<ExpressionNode> mpFormula;
+
+ std::shared_ptr<AnimationType> mpAnim;
+ Interpolator< ValueType > maInterpolator;
+ bool mbCumulative;
+};
+
+/** Generate Activity corresponding to given Value vector
+
+ @tpl BaseType
+ BaseType to use for deriving the Activity from
+
+ @tpl AnimationType
+ Subtype of the Animation object (e.g. NumberAnimation)
+*/
+template<class BaseType, typename AnimationType>
+AnimationActivitySharedPtr createValueListActivity(
+ const uno::Sequence<uno::Any>& rValues,
+ const ActivityParameters& rParms,
+ const std::shared_ptr<AnimationType>& rAnim,
+ const Interpolator<typename AnimationType::ValueType>& rInterpolator,
+ bool bCumulative,
+ const ShapeSharedPtr& rShape,
+ const ::basegfx::B2DVector& rSlideBounds )
+{
+ typedef typename AnimationType::ValueType ValueType;
+ typedef std::vector<ValueType> ValueVectorType;
+
+ ValueVectorType aValueVector;
+ aValueVector.reserve( rValues.getLength() );
+
+ for( const auto& rValue : rValues )
+ {
+ ValueType aValue;
+ ENSURE_OR_THROW(
+ extractValue( aValue, rValue, rShape, rSlideBounds ),
+ "createValueListActivity(): Could not extract values" );
+ aValueVector.push_back( aValue );
+ }
+
+ return std::make_shared<ValuesActivity<BaseType, AnimationType>>(
+ aValueVector,
+ rParms,
+ rAnim,
+ rInterpolator,
+ bCumulative );
+}
+
+/** Generate Activity for given XAnimate, corresponding to given Value vector
+
+ @tpl AnimationType
+ Subtype of the Animation object (e.g. NumberAnimation)
+
+ @param rParms
+ Common activity parameters
+
+ @param xNode
+ XAnimate node, to retrieve animation values from
+
+ @param rAnim
+ Actual animation to operate with (gets called with the
+ time-dependent values)
+
+ @param rInterpolator
+ Interpolator object to be used for lerping between
+ start and end values (need to be passed, since it
+ might contain state, e.g. interpolation direction
+ for HSL color space).
+*/
+template<typename AnimationType>
+AnimationActivitySharedPtr createActivity(
+ const ActivitiesFactory::CommonParameters& rParms,
+ const uno::Reference< animations::XAnimate >& xNode,
+ const ::std::shared_ptr< AnimationType >& rAnim,
+ const Interpolator< typename AnimationType::ValueType >& rInterpolator
+ = Interpolator< typename AnimationType::ValueType >() )
+{
+ // setup common parameters
+ // =======================
+
+ ActivityParameters aActivityParms( rParms.mpEndEvent,
+ rParms.mrEventQueue,
+ rParms.mrActivitiesQueue,
+ rParms.mnMinDuration,
+ rParms.maRepeats,
+ rParms.mnAcceleration,
+ rParms.mnDeceleration,
+ rParms.mnMinNumberOfFrames,
+ rParms.mbAutoReverse );
+
+ // is a formula given?
+ const OUString& rFormulaString( xNode->getFormula() );
+ if( !rFormulaString.isEmpty() )
+ {
+ // yep, parse and pass to ActivityParameters
+ try
+ {
+ aActivityParms.mpFormula =
+ SmilFunctionParser::parseSmilFunction(
+ rFormulaString,
+ calcRelativeShapeBounds(
+ rParms.maSlideBounds,
+ rParms.mpShape->getBounds() ) );
+ }
+ catch( ParseError& )
+ {
+ // parse error, thus no formula
+ OSL_FAIL( "createActivity(): Error parsing formula string" );
+ }
+ }
+
+ // are key times given?
+ const uno::Sequence< double >& aKeyTimes( xNode->getKeyTimes() );
+ if( aKeyTimes.hasElements() )
+ {
+ // yes, convert them from Sequence< double >
+ aActivityParms.maDiscreteTimes.resize( aKeyTimes.getLength() );
+ comphelper::sequenceToArray(
+ aActivityParms.maDiscreteTimes.data(),
+ aKeyTimes ); // saves us some temporary vectors
+ }
+
+ // values sequence given?
+ const sal_Int32 nValueLen( xNode->getValues().getLength() );
+ if( nValueLen )
+ {
+ // Value list activity
+ // ===================
+
+ // fake keytimes, if necessary
+ if( !aKeyTimes.hasElements() )
+ {
+ // create a dummy vector of key times,
+ // with aValues.getLength equally spaced entries.
+ for( sal_Int32 i=0; i<nValueLen; ++i )
+ aActivityParms.maDiscreteTimes.push_back( double(i)/nValueLen );
+ }
+
+ // determine type of animation needed here:
+ // Value list activities are possible with
+ // ContinuousKeyTimeActivityBase and DiscreteActivityBase
+ // specializations
+ const sal_Int16 nCalcMode( xNode->getCalcMode() );
+
+ switch( nCalcMode )
+ {
+ case animations::AnimationCalcMode::DISCRETE:
+ {
+ // since DiscreteActivityBase suspends itself
+ // between the frames, create a WakeupEvent for it.
+ aActivityParms.mpWakeupEvent =
+ std::make_shared<WakeupEvent>(
+ rParms.mrEventQueue.getTimer(),
+ rParms.mrActivitiesQueue );
+
+ AnimationActivitySharedPtr pActivity(
+ createValueListActivity< DiscreteActivityBase >(
+ xNode->getValues(),
+ aActivityParms,
+ rAnim,
+ rInterpolator,
+ xNode->getAccumulate(),
+ rParms.mpShape,
+ rParms.maSlideBounds ) );
+
+ // WakeupEvent and DiscreteActivityBase need circular
+ // references to the corresponding other object.
+ aActivityParms.mpWakeupEvent->setActivity( pActivity );
+
+ return pActivity;
+ }
+
+ default:
+ OSL_FAIL( "createActivity(): unexpected case" );
+ [[fallthrough]];
+ case animations::AnimationCalcMode::PACED:
+ case animations::AnimationCalcMode::SPLINE:
+ case animations::AnimationCalcMode::LINEAR:
+ return createValueListActivity< ContinuousKeyTimeActivityBase >(
+ xNode->getValues(),
+ aActivityParms,
+ rAnim,
+ rInterpolator,
+ xNode->getAccumulate(),
+ rParms.mpShape,
+ rParms.maSlideBounds );
+ }
+ }
+ else
+ {
+ // FromToBy activity
+ // =================
+
+ // determine type of animation needed here:
+ // FromToBy activities are possible with
+ // ContinuousActivityBase and DiscreteActivityBase
+ // specializations
+ const sal_Int16 nCalcMode( xNode->getCalcMode() );
+
+ switch( nCalcMode )
+ {
+ case animations::AnimationCalcMode::DISCRETE:
+ {
+ // fake keytimes, if necessary
+ if( !aKeyTimes.hasElements() )
+ {
+ // create a dummy vector of 2 key times
+ const ::std::size_t nLen( 2 );
+ for( ::std::size_t i=0; i<nLen; ++i )
+ aActivityParms.maDiscreteTimes.push_back( double(i)/nLen );
+ }
+
+ // since DiscreteActivityBase suspends itself
+ // between the frames, create a WakeupEvent for it.
+ aActivityParms.mpWakeupEvent =
+ std::make_shared<WakeupEvent>(
+ rParms.mrEventQueue.getTimer(),
+ rParms.mrActivitiesQueue );
+
+ AnimationActivitySharedPtr pActivity(
+ createFromToByActivity< DiscreteActivityBase >(
+ xNode->getFrom(),
+ xNode->getTo(),
+ xNode->getBy(),
+ aActivityParms,
+ rAnim,
+ rInterpolator,
+ xNode->getAccumulate(),
+ rParms.mpShape,
+ rParms.maSlideBounds ) );
+
+ // WakeupEvent and DiscreteActivityBase need circular
+ // references to the corresponding other object.
+ aActivityParms.mpWakeupEvent->setActivity( pActivity );
+
+ return pActivity;
+ }
+
+ default:
+ OSL_FAIL( "createActivity(): unexpected case" );
+ [[fallthrough]];
+ case animations::AnimationCalcMode::PACED:
+ case animations::AnimationCalcMode::SPLINE:
+ case animations::AnimationCalcMode::LINEAR:
+ return createFromToByActivity< ContinuousActivityBase >(
+ xNode->getFrom(),
+ xNode->getTo(),
+ xNode->getBy(),
+ aActivityParms,
+ rAnim,
+ rInterpolator,
+ xNode->getAccumulate(),
+ rParms.mpShape,
+ rParms.maSlideBounds );
+ }
+ }
+}
+
+/** Simple activity for ActivitiesFactory::createSimpleActivity
+
+ @tpl Direction
+ Determines direction of value generator. A 1 yields a
+ forward direction, starting with 0.0 and ending with
+ 1.0. A 0 yields a backward direction, starting with
+ 1.0 and ending with 0.0
+*/
+template<int Direction>
+class SimpleActivity : public ContinuousActivityBase
+{
+public:
+ /** Create SimpleActivity.
+
+ @param rParms
+ Standard Activity parameter struct
+ */
+ SimpleActivity( const ActivityParameters& rParms,
+ const NumberAnimationSharedPtr& rAnim ) :
+ ContinuousActivityBase( rParms ),
+ mpAnim( rAnim )
+ {
+ ENSURE_OR_THROW( mpAnim, "Invalid animation object" );
+ }
+
+ virtual void startAnimation() override
+ {
+ if (this->isDisposed() || !mpAnim)
+ return;
+ ContinuousActivityBase::startAnimation();
+
+ // start animation
+ mpAnim->start( getShape(),
+ getShapeAttributeLayer() );
+ }
+
+ virtual void endAnimation() override
+ {
+ // end animation
+ if (mpAnim)
+ mpAnim->end();
+ }
+
+ using SimpleContinuousActivityBase::perform;
+
+ /// perform override for ContinuousActivityBase
+ virtual void perform( double nModifiedTime, sal_uInt32 ) const override
+ {
+ if (this->isDisposed() || !mpAnim)
+ return;
+ // no cumulation, simple [0,1] range
+ (*mpAnim)( 1.0 - Direction + nModifiedTime*(2.0*Direction - 1.0) );
+ }
+
+ virtual void performEnd() override
+ {
+ // xxx todo: review
+ if (mpAnim)
+ (*mpAnim)( 1.0*Direction );
+ }
+
+ /// Disposable:
+ virtual void dispose() override
+ {
+ mpAnim.reset();
+ ContinuousActivityBase::dispose();
+ }
+
+private:
+ NumberAnimationSharedPtr mpAnim;
+};
+
+} // anon namespace
+
+
+AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
+ const CommonParameters& rParms,
+ const NumberAnimationSharedPtr& rAnim,
+ const uno::Reference< animations::XAnimate >& xNode )
+{
+ // forward to appropriate template instantiation
+ return createActivity( rParms, xNode, rAnim );
+}
+
+AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
+ const CommonParameters& rParms,
+ const EnumAnimationSharedPtr& rAnim,
+ const uno::Reference< animations::XAnimate >& xNode )
+{
+ // forward to appropriate template instantiation
+ return createActivity( rParms, xNode, rAnim );
+}
+
+AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
+ const CommonParameters& rParms,
+ const ColorAnimationSharedPtr& rAnim,
+ const uno::Reference< animations::XAnimate >& xNode )
+{
+ // forward to appropriate template instantiation
+ return createActivity( rParms, xNode, rAnim );
+}
+
+AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
+ const CommonParameters& rParms,
+ const HSLColorAnimationSharedPtr& rAnim,
+ const uno::Reference< animations::XAnimateColor >& xNode )
+{
+ // forward to appropriate template instantiation
+ return createActivity( rParms,
+ uno::Reference< animations::XAnimate >(
+ xNode, uno::UNO_QUERY_THROW ),
+ rAnim,
+ // Direction==true means clockwise in SMIL API
+ Interpolator< HSLColor >( !xNode->getDirection() ) );
+}
+
+AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
+ const CommonParameters& rParms,
+ const PairAnimationSharedPtr& rAnim,
+ const uno::Reference< animations::XAnimate >& xNode )
+{
+ // forward to appropriate template instantiation
+ return createActivity( rParms, xNode, rAnim );
+}
+
+AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
+ const CommonParameters& rParms,
+ const StringAnimationSharedPtr& rAnim,
+ const uno::Reference< animations::XAnimate >& xNode )
+{
+ // forward to appropriate template instantiation
+ return createActivity( rParms, xNode, rAnim );
+}
+
+AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
+ const CommonParameters& rParms,
+ const BoolAnimationSharedPtr& rAnim,
+ const uno::Reference< animations::XAnimate >& xNode )
+{
+ // forward to appropriate template instantiation
+ return createActivity( rParms, xNode, rAnim );
+}
+
+AnimationActivitySharedPtr ActivitiesFactory::createSimpleActivity(
+ const CommonParameters& rParms,
+ const NumberAnimationSharedPtr& rAnim,
+ bool bDirectionForward )
+{
+ ActivityParameters aActivityParms( rParms.mpEndEvent,
+ rParms.mrEventQueue,
+ rParms.mrActivitiesQueue,
+ rParms.mnMinDuration,
+ rParms.maRepeats,
+ rParms.mnAcceleration,
+ rParms.mnDeceleration,
+ rParms.mnMinNumberOfFrames,
+ rParms.mbAutoReverse );
+
+ if( bDirectionForward )
+ return std::make_shared<SimpleActivity<1>>( aActivityParms, rAnim );
+ else
+ return std::make_shared<SimpleActivity<0>>( aActivityParms, rAnim );
+}
+
+} // namespace presentation
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/activitybase.cxx b/slideshow/source/engine/activities/activitybase.cxx
new file mode 100644
index 000000000..82a117f8a
--- /dev/null
+++ b/slideshow/source/engine/activities/activitybase.cxx
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <tools/diagnose_ex.h>
+
+#include "activitybase.hxx"
+
+
+namespace slideshow::internal
+{
+ // TODO(P1): Elide some virtual function calls, by templifying this
+ // static hierarchy
+
+ ActivityBase::ActivityBase( const ActivityParameters& rParms ) :
+ mpEndEvent( rParms.mrEndEvent ),
+ mrEventQueue( rParms.mrEventQueue ),
+ mpShape(),
+ mpAttributeLayer(),
+ maRepeats( rParms.mrRepeats ),
+ mnAccelerationFraction( rParms.mnAccelerationFraction ),
+ mnDecelerationFraction( rParms.mnDecelerationFraction ),
+ mbAutoReverse( rParms.mbAutoReverse ),
+ mbFirstPerformCall( true ),
+ mbIsActive( true ) {}
+
+ void ActivityBase::dispose()
+ {
+ // deactivate
+ mbIsActive = false;
+
+ // dispose event
+ if( mpEndEvent )
+ mpEndEvent->dispose();
+
+ // release references
+ mpEndEvent.reset();
+ mpShape.reset();
+ mpAttributeLayer.reset();
+ }
+
+ double ActivityBase::calcTimeLag() const
+ {
+ // TODO(Q1): implement different init process!
+ if (isActive() && mbFirstPerformCall)
+ {
+ mbFirstPerformCall = false;
+
+ // notify derived classes that we're
+ // starting now
+ const_cast<ActivityBase *>(this)->startAnimation();
+ }
+ return 0.0;
+ }
+
+ bool ActivityBase::perform()
+ {
+ // still active?
+ if( !isActive() )
+ return false; // no, early exit.
+
+ OSL_ASSERT( ! mbFirstPerformCall );
+
+ return true;
+ }
+
+ bool ActivityBase::isActive() const
+ {
+ return mbIsActive;
+ }
+
+ void ActivityBase::setTargets( const AnimatableShapeSharedPtr& rShape,
+ const ShapeAttributeLayerSharedPtr& rAttrLayer )
+ {
+ ENSURE_OR_THROW( rShape,
+ "ActivityBase::setTargets(): Invalid shape" );
+ ENSURE_OR_THROW( rAttrLayer,
+ "ActivityBase::setTargets(): Invalid attribute layer" );
+
+ mpShape = rShape;
+ mpAttributeLayer = rAttrLayer;
+ }
+
+ void ActivityBase::endActivity()
+ {
+ // this is a regular activity end
+ mbIsActive = false;
+
+ // Activity is ending, queue event, then
+ if( mpEndEvent )
+ mrEventQueue.addEvent( mpEndEvent );
+
+ // release references
+ mpEndEvent.reset();
+ }
+
+ void ActivityBase::dequeued()
+ {
+ // xxx todo:
+// // ignored here, if we're still active. Discrete
+// // activities are dequeued after every perform() call,
+// // thus, the call is only significant when isActive() ==
+// // false.
+ if( !isActive() )
+ endAnimation();
+ }
+
+ void ActivityBase::end()
+ {
+ if (!isActive() || isDisposed())
+ return;
+ // assure animation is started:
+ if (mbFirstPerformCall) {
+ mbFirstPerformCall = false;
+ // notify derived classes that we're starting now
+ startAnimation();
+ }
+
+ performEnd(); // calling private virtual
+ endAnimation();
+ endActivity();
+ }
+
+ double ActivityBase::calcAcceleratedTime( double nT ) const
+ {
+ // Handle acceleration/deceleration
+ // ================================
+
+ // clamp nT to permissible [0,1] range
+ nT = std::clamp( nT, 0.0, 1.0 );
+
+ // take acceleration/deceleration into account. if the sum
+ // of mnAccelerationFraction and mnDecelerationFraction
+ // exceeds 1.0, ignore both (that's according to SMIL spec)
+ if( (mnAccelerationFraction > 0.0 ||
+ mnDecelerationFraction > 0.0) &&
+ mnAccelerationFraction + mnDecelerationFraction <= 1.0 )
+ {
+ /*
+ // calc accelerated/decelerated time.
+
+ // We have three intervals:
+ // 1 [0,a]
+ // 2 [a,d]
+ // 3 [d,1] (with a and d being acceleration/deceleration
+ // fraction, resp.)
+
+ // The change rate during interval 1 is constantly
+ // increasing, reaching 1 at a. It then stays at 1,
+ // starting a linear decrease at d, ending with 0 at
+ // time 1. The integral of this function is the
+ // required new time nT'.
+
+ // As we arbitrarily assumed 1 as the upper value of
+ // the change rate, the integral must be normalized to
+ // reach nT'=1 at the end of the interval. This
+ // normalization constant is:
+
+ // c = 1 - 0.5a - 0.5d
+
+ // The integral itself then amounts to:
+
+ // 0.5 nT^2 / a + (nT-a) + (nT - 0.5 nT^2 / d)
+
+ // (where each of the three summands correspond to the
+ // three intervals above, and are applied only if nT
+ // has reached the corresponding interval)
+
+ // The graph of the change rate is a trapezoid:
+
+ // |
+ // 1| /--------------\
+ // | / \
+ // | / \
+ // | / \
+ // -----------------------------
+ // 0 a d 1
+
+ //*/
+ const double nC( 1.0 - 0.5*mnAccelerationFraction - 0.5*mnDecelerationFraction );
+
+ // this variable accumulates the new time value
+ double nTPrime(0.0);
+
+ if( nT < mnAccelerationFraction )
+ {
+ nTPrime += 0.5*nT*nT/mnAccelerationFraction; // partial first interval
+ }
+ else
+ {
+ nTPrime += 0.5*mnAccelerationFraction; // full first interval
+
+ if( nT <= 1.0-mnDecelerationFraction )
+ {
+ nTPrime += nT-mnAccelerationFraction; // partial second interval
+ }
+ else
+ {
+ nTPrime += 1.0 - mnAccelerationFraction - mnDecelerationFraction; // full second interval
+
+ const double nTRelative( nT - 1.0 + mnDecelerationFraction );
+
+ nTPrime += nTRelative - 0.5*nTRelative*nTRelative / mnDecelerationFraction;
+ }
+ }
+
+ // normalize, and assign to work variable
+ nT = nTPrime / nC;
+ }
+
+ return nT;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/activitybase.hxx b/slideshow/source/engine/activities/activitybase.hxx
new file mode 100644
index 000000000..e76c84d8e
--- /dev/null
+++ b/slideshow/source/engine/activities/activitybase.hxx
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_ACTIVITYBASE_HXX
+#define INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_ACTIVITYBASE_HXX
+
+#include <animationactivity.hxx>
+#include "activityparameters.hxx"
+#include <animatableshape.hxx>
+#include <shapeattributelayer.hxx>
+
+namespace slideshow::internal {
+
+/** Base class for animation activities.
+
+ This whole class hierarchy is only for code sharing
+ between the various specializations (with or without
+ key times, fully discrete, etc.).
+*/
+class ActivityBase : public AnimationActivity
+{
+public:
+ explicit ActivityBase( const ActivityParameters& rParms );
+
+ /// From Disposable interface
+ virtual void dispose() override;
+
+protected:
+ /** From Activity interface
+
+ Derived classes should override, call this first
+ and then perform their work.
+ */
+ virtual bool perform() override;
+ virtual double calcTimeLag() const override;
+ virtual bool isActive() const override;
+
+private:
+ virtual void dequeued() override;
+
+ // From AnimationActivity interface
+ virtual void setTargets(
+ const AnimatableShapeSharedPtr& rShape,
+ const ShapeAttributeLayerSharedPtr& rAttrLayer ) override;
+
+private:
+ /** Hook for derived classes
+
+ This method will be called from the first
+ perform() invocation, to signal the start of the
+ activity.
+ */
+ virtual void startAnimation() = 0;
+
+ /** Hook for derived classes
+
+ This method will be called after the last perform()
+ invocation, and after the potential changes of that
+ perform() call are committed to screen. That is, in
+ endAnimation(), the animation objects (sprites,
+ animation) can safely be destroyed, without causing
+ visible artifacts on screen.
+ */
+ virtual void endAnimation() = 0;
+
+protected:
+
+ /** End this activity, in a regular way.
+
+ This method is for derived classes needing to signal a
+ regular activity end (i.e. because the regular
+ duration is over)
+ */
+ void endActivity();
+
+ /** Modify fractional time.
+
+ This method modifies the fractional time (total
+ duration mapped to the [0,1] range) to the
+ effective simple time, but only according to
+ acceleration/deceleration.
+ */
+ double calcAcceleratedTime( double nT ) const;
+
+ bool isDisposed() const {
+ return (!mbIsActive && !mpEndEvent && !mpShape &&
+ !mpAttributeLayer);
+ }
+
+ EventQueue& getEventQueue() const { return mrEventQueue; }
+
+ const AnimatableShapeSharedPtr& getShape() const { return mpShape; }
+
+ const ShapeAttributeLayerSharedPtr& getShapeAttributeLayer() const
+ { return mpAttributeLayer; }
+
+ bool isRepeatCountValid() const { return bool(maRepeats); }
+ double getRepeatCount() const { return *maRepeats; }
+ bool isAutoReverse() const { return mbAutoReverse; }
+
+private:
+ /// Activity:
+ virtual void end() override;
+ virtual void performEnd() = 0;
+
+private:
+ EventSharedPtr mpEndEvent;
+ EventQueue& mrEventQueue;
+ AnimatableShapeSharedPtr mpShape; // only to pass on to animation
+ ShapeAttributeLayerSharedPtr mpAttributeLayer; // only to pass on to anim
+
+ ::std::optional<double> const maRepeats;
+ const double mnAccelerationFraction;
+ const double mnDecelerationFraction;
+
+ const bool mbAutoReverse;
+
+ // true, if perform() has not yet been called:
+ mutable bool mbFirstPerformCall;
+ bool mbIsActive;
+};
+
+} // namespace presentation::internal
+
+#endif // INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_ACTIVITYBASE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/activityparameters.hxx b/slideshow/source/engine/activities/activityparameters.hxx
new file mode 100644
index 000000000..9df762838
--- /dev/null
+++ b/slideshow/source/engine/activities/activityparameters.hxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_ACTIVITYPARAMETERS_HXX
+#define INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_ACTIVITYPARAMETERS_HXX
+
+#include <event.hxx>
+#include <eventqueue.hxx>
+#include <expressionnode.hxx>
+#include <wakeupevent.hxx>
+
+#include <optional>
+#include <vector>
+
+namespace slideshow::internal {
+
+/** Parameter struct for animation activities
+
+ This struct contains all common parameters needed to
+ initialize the activities generated by the ActivityFactory.
+*/
+struct ActivityParameters
+{
+ /** Create
+
+ @param rEndEvent
+ Event to be fired, when the activity ends.
+
+ @param rEventQueue
+ Queue to add end event to
+
+ @param nMinDuration
+ Minimal duration of the activity (might actually be
+ longer because of nMinNumberOfFrames). Note that this
+ duration must always be the <em>simple</em> duration,
+ i.e. without any repeat.
+
+ @param rRepeats
+ Number of repeats. If this parameter is invalid,
+ infinite repeat is assumed.
+
+ @param nAccelerationFraction
+ Value between 0 and 1, denoting the fraction of the
+ total simple duration, which the animation should
+ accelerate.
+
+ @param nDecelerationFraction
+ Value between 0 and 1, denoting the fraction of the
+ total simple duration, which the animation should
+ decelerate. Note that the ranges
+ [0,nAccelerationFraction] and
+ [nDecelerationFraction,1] must be non-overlapping!
+
+ @param bAutoReverse
+ When true, at the end of the simple duration, the
+ animation plays reversed to the start value. Note that
+ nMinDuration still specifies the simple duration,
+ i.e. when bAutoReverse is true, the implicit duration
+ doubles.
+ */
+ ActivityParameters(
+ const EventSharedPtr& rEndEvent,
+ EventQueue& rEventQueue,
+ ActivitiesQueue& rActivitiesQueue,
+ double nMinDuration,
+ ::std::optional<double> const& rRepeats,
+ double nAccelerationFraction,
+ double nDecelerationFraction,
+ sal_uInt32 nMinNumberOfFrames,
+ bool bAutoReverse )
+ : mrEndEvent( rEndEvent ),
+ mpWakeupEvent(),
+ mrEventQueue( rEventQueue ),
+ mrActivitiesQueue( rActivitiesQueue ),
+ mpFormula(),
+ maDiscreteTimes(),
+ mnMinDuration( nMinDuration ),
+ mrRepeats( rRepeats ),
+ mnAccelerationFraction( nAccelerationFraction ),
+ mnDecelerationFraction( nDecelerationFraction ),
+ mnMinNumberOfFrames( nMinNumberOfFrames ),
+ mbAutoReverse( bAutoReverse ) {}
+
+ /// End event to fire, when activity is over
+ const EventSharedPtr& mrEndEvent;
+ /// Wakeup event to use for discrete activities
+ WakeupEventSharedPtr mpWakeupEvent;
+
+ /// EventQueue to add events to
+ EventQueue& mrEventQueue;
+
+ /// ActivitiesQueue to add events to
+ ActivitiesQueue& mrActivitiesQueue;
+
+ /// Optional formula
+ std::shared_ptr<ExpressionNode> mpFormula;
+
+ /// Key times, for discrete and key time activities
+ ::std::vector< double > maDiscreteTimes;
+
+ /// Total duration of activity (including all repeats)
+ const double mnMinDuration;
+ ::std::optional<double> const& mrRepeats;
+ const double mnAccelerationFraction;
+ const double mnDecelerationFraction;
+
+ /// Minimal number of frames this activity must render
+ const sal_uInt32 mnMinNumberOfFrames;
+
+ /// When true, activity is played reversed after mnDuration.
+ const bool mbAutoReverse;
+};
+
+} // namespace presentation::internal
+
+#endif // INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_ACTIVITYPARAMETERS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/continuousactivitybase.cxx b/slideshow/source/engine/activities/continuousactivitybase.cxx
new file mode 100644
index 000000000..64e2377f9
--- /dev/null
+++ b/slideshow/source/engine/activities/continuousactivitybase.cxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "continuousactivitybase.hxx"
+
+
+namespace slideshow::internal
+{
+ ContinuousActivityBase::ContinuousActivityBase( const ActivityParameters& rParms ) :
+ SimpleContinuousActivityBase( rParms )
+ {
+ }
+
+ void ContinuousActivityBase::simplePerform( double nSimpleTime,
+ sal_uInt32 nRepeatCount ) const
+ {
+ perform( calcAcceleratedTime( nSimpleTime ),
+ nRepeatCount );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/continuousactivitybase.hxx b/slideshow/source/engine/activities/continuousactivitybase.hxx
new file mode 100644
index 000000000..8fd19d421
--- /dev/null
+++ b/slideshow/source/engine/activities/continuousactivitybase.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_CONTINUOUSACTIVITYBASE_HXX
+#define INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_CONTINUOUSACTIVITYBASE_HXX
+
+#include "simplecontinuousactivitybase.hxx"
+
+namespace slideshow::internal
+ {
+ /** Simple, continuous animation.
+
+ This class implements a simple, continuous
+ animation. Only addition to ActivityBase class is an
+ explicit animation duration and a minimal number of
+ frames to display.
+ */
+ class ContinuousActivityBase : public SimpleContinuousActivityBase
+ {
+ public:
+ explicit ContinuousActivityBase( const ActivityParameters& rParms );
+
+ using SimpleContinuousActivityBase::perform;
+
+ /** Hook for derived classes
+
+ This method will be called from perform(), already
+ equipped with the modified time (nMinNumberOfFrames, repeat,
+ acceleration and deceleration taken into account).
+
+ @param nModifiedTime
+ Already accelerated/decelerated and repeated time, always
+ in the [0,1] range.
+
+ @param nRepeatCount
+ Number of full repeats already performed
+ */
+ virtual void perform( double nModifiedTime, sal_uInt32 nRepeatCount ) const = 0;
+
+ /// From SimpleContinuousActivityBase class
+ virtual void simplePerform( double nSimpleTime,
+ sal_uInt32 nRepeatCount ) const override;
+ };
+
+}
+
+#endif // INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_CONTINUOUSACTIVITYBASE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/continuouskeytimeactivitybase.cxx b/slideshow/source/engine/activities/continuouskeytimeactivitybase.cxx
new file mode 100644
index 000000000..db25d2dd4
--- /dev/null
+++ b/slideshow/source/engine/activities/continuouskeytimeactivitybase.cxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <tools/diagnose_ex.h>
+
+#include "continuouskeytimeactivitybase.hxx"
+
+#include <tuple>
+
+namespace slideshow::internal
+{
+ ContinuousKeyTimeActivityBase::ContinuousKeyTimeActivityBase( const ActivityParameters& rParms ) :
+ SimpleContinuousActivityBase( rParms ),
+ maLerper( std::vector(rParms.maDiscreteTimes) )
+ {
+ ENSURE_OR_THROW( rParms.maDiscreteTimes.size() > 1,
+ "ContinuousKeyTimeActivityBase::ContinuousKeyTimeActivityBase(): key times vector must have two entries or more" );
+ ENSURE_OR_THROW( rParms.maDiscreteTimes.front() == 0.0,
+ "ContinuousKeyTimeActivityBase::ContinuousKeyTimeActivityBase(): key times vector first entry must be zero" );
+ ENSURE_OR_THROW( rParms.maDiscreteTimes.back() <= 1.0,
+ "ContinuousKeyTimeActivityBase::ContinuousKeyTimeActivityBase(): key times vector last entry must be less or equal 1" );
+ }
+
+ void ContinuousKeyTimeActivityBase::simplePerform( double nSimpleTime,
+ sal_uInt32 nRepeatCount ) const
+ {
+ // calc simple time from global time - sweep through the
+ // array multiple times for repeated animations (according to
+ // SMIL spec).
+ double fAlpha( calcAcceleratedTime( nSimpleTime ) );
+ std::ptrdiff_t nIndex;
+
+ std::tie(nIndex,fAlpha) = maLerper.lerp(fAlpha);
+
+ perform(
+ nIndex,
+ fAlpha,
+ nRepeatCount );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/continuouskeytimeactivitybase.hxx b/slideshow/source/engine/activities/continuouskeytimeactivitybase.hxx
new file mode 100644
index 000000000..2238db228
--- /dev/null
+++ b/slideshow/source/engine/activities/continuouskeytimeactivitybase.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_CONTINUOUSKEYTIMEACTIVITYBASE_HXX
+#define INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_CONTINUOUSKEYTIMEACTIVITYBASE_HXX
+
+#include "simplecontinuousactivitybase.hxx"
+
+#include <basegfx/utils/keystoplerp.hxx>
+
+
+namespace slideshow::internal
+ {
+ /** Interpolated, key-times animation.
+
+ This class implements an interpolated key-times
+ animation, with continuous time.
+ */
+ class ContinuousKeyTimeActivityBase : public SimpleContinuousActivityBase
+ {
+ public:
+ explicit ContinuousKeyTimeActivityBase( const ActivityParameters& rParms );
+
+ using SimpleContinuousActivityBase::perform;
+
+ /** Hook for derived classes
+
+ This method will be called from perform(), already
+ equipped with the modified time (nMinNumberOfFrames, repeat,
+ acceleration and deceleration taken into account).
+
+ @param nIndex
+ Current index of the key times/key values.
+
+ @param nFractionalIndex
+ Fractional value from the [0,1] range, specifying
+ the position between nIndex and nIndex+1.
+
+ @param nRepeatCount
+ Number of full repeats already performed
+ */
+ virtual void perform( sal_uInt32 nIndex,
+ double nFractionalIndex,
+ sal_uInt32 nRepeatCount ) const = 0;
+
+ /// From SimpleContinuousActivityBase class
+ virtual void simplePerform( double nSimpleTime,
+ sal_uInt32 nRepeatCount ) const override;
+
+ private:
+ const ::basegfx::utils::KeyStopLerp maLerper;
+ };
+
+}
+
+#endif // INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_CONTINUOUSKEYTIMEACTIVITYBASE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/discreteactivitybase.cxx b/slideshow/source/engine/activities/discreteactivitybase.cxx
new file mode 100644
index 000000000..2fe0935b6
--- /dev/null
+++ b/slideshow/source/engine/activities/discreteactivitybase.cxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <tools/diagnose_ex.h>
+
+#include "discreteactivitybase.hxx"
+
+
+namespace slideshow::internal
+{
+ DiscreteActivityBase::DiscreteActivityBase( const ActivityParameters& rParms ) :
+ ActivityBase( rParms ),
+ mpWakeupEvent( rParms.mpWakeupEvent ),
+ maDiscreteTimes( rParms.maDiscreteTimes ),
+ mnSimpleDuration( rParms.mnMinDuration ),
+ mnCurrPerformCalls( 0 )
+ {
+ ENSURE_OR_THROW( mpWakeupEvent,
+ "DiscreteActivityBase::DiscreteActivityBase(): Invalid wakeup event" );
+
+ ENSURE_OR_THROW( !maDiscreteTimes.empty(),
+ "DiscreteActivityBase::DiscreteActivityBase(): time vector is empty, why do you create me?" );
+
+#ifdef DBG_UTIL
+ // check parameters: rDiscreteTimes must be sorted in
+ // ascending order, and contain values only from the range
+ // [0,1]
+ for( ::std::size_t i=1, len=maDiscreteTimes.size(); i<len; ++i )
+ {
+ if( maDiscreteTimes[i] < 0.0 ||
+ maDiscreteTimes[i] > 1.0 ||
+ maDiscreteTimes[i-1] < 0.0 ||
+ maDiscreteTimes[i-1] > 1.0 )
+ {
+ ENSURE_OR_THROW( false, "DiscreteActivityBase::DiscreteActivityBase(): time values not within [0,1] range!" );
+ }
+
+ if( maDiscreteTimes[i-1] > maDiscreteTimes[i] )
+ ENSURE_OR_THROW( false, "DiscreteActivityBase::DiscreteActivityBase(): time vector is not sorted in ascending order!" );
+ }
+
+ // TODO(E2): check this also in production code?
+#endif
+ }
+
+ void DiscreteActivityBase::startAnimation()
+ {
+ // start timer on wakeup event
+ mpWakeupEvent->start();
+ }
+
+ sal_uInt32 DiscreteActivityBase::calcFrameIndex( sal_uInt32 nCurrCalls,
+ ::std::size_t nVectorSize ) const
+ {
+ if( isAutoReverse() )
+ {
+ // every full repeat run consists of one
+ // forward and one backward traversal.
+ sal_uInt32 nFrameIndex( nCurrCalls % (2*nVectorSize) );
+
+ // nFrameIndex values >= nVectorSize belong to
+ // the backward traversal
+ if( nFrameIndex >= nVectorSize )
+ nFrameIndex = 2*nVectorSize - nFrameIndex; // invert sweep
+
+ return nFrameIndex;
+ }
+ else
+ {
+ return nCurrCalls % nVectorSize ;
+ }
+ }
+
+ sal_uInt32 DiscreteActivityBase::calcRepeatCount( sal_uInt32 nCurrCalls,
+ ::std::size_t nVectorSize ) const
+ {
+ if( isAutoReverse() )
+ return nCurrCalls / (2*nVectorSize); // we've got 2 cycles per repeat
+ else
+ return nCurrCalls / nVectorSize;
+ }
+
+ bool DiscreteActivityBase::perform()
+ {
+ // call base class, for start() calls and end handling
+ if( !ActivityBase::perform() )
+ return false; // done, we're ended
+
+ const ::std::size_t nVectorSize( maDiscreteTimes.size() );
+
+ // actually perform something
+ // ==========================
+
+ // TODO(Q3): Refactor this mess
+
+ // call derived class with current frame index (modulo
+ // vector size, to cope with repeats)
+ perform( calcFrameIndex( mnCurrPerformCalls, nVectorSize ),
+ calcRepeatCount( mnCurrPerformCalls, nVectorSize ) );
+
+ // calc next index
+ ++mnCurrPerformCalls;
+
+ // calc currently reached repeat count
+ double nCurrRepeat( double(mnCurrPerformCalls) / nVectorSize );
+
+ // if auto-reverse is specified, halve the
+ // effective repeat count, since we pass every
+ // repeat run twice: once forward, once backward.
+ if( isAutoReverse() )
+ nCurrRepeat /= 2.0;
+
+ // schedule next frame, if either repeat is indefinite
+ // (repeat forever), or we've not yet reached the requested
+ // repeat count
+ if( !isRepeatCountValid() ||
+ nCurrRepeat < getRepeatCount() )
+ {
+ // add wake-up event to queue (modulo
+ // vector size, to cope with repeats).
+
+ // repeat is handled locally, only apply acceleration/deceleration.
+ // Scale time vector with simple duration, offset with full repeat
+ // times.
+
+ // Somewhat condensed, the argument for setNextTimeout below could
+ // be written as
+
+ // mnSimpleDuration*(nFullRepeats + calcAcceleratedTime( currentRepeatTime )),
+
+ // with currentRepeatTime = maDiscreteTimes[ currentRepeatIndex ]
+
+ // Note that calcAcceleratedTime() is only applied to the current repeat's value,
+ // not to the total resulting time. This is in accordance with the SMIL spec.
+
+ mpWakeupEvent->setNextTimeout(
+ mnSimpleDuration*(
+ calcRepeatCount(
+ mnCurrPerformCalls,
+ nVectorSize ) +
+ calcAcceleratedTime(
+ maDiscreteTimes[
+ calcFrameIndex(
+ mnCurrPerformCalls,
+ nVectorSize ) ] ) ) );
+
+ getEventQueue().addEvent( mpWakeupEvent );
+ }
+ else
+ {
+ // release event reference (relation to wakeup event
+ // is circular!)
+ mpWakeupEvent.reset();
+
+ // done with this activity
+ endActivity();
+ }
+
+ return false; // remove from queue, will be added back by the wakeup event.
+ }
+
+ void DiscreteActivityBase::dispose()
+ {
+ // dispose event
+ if( mpWakeupEvent )
+ mpWakeupEvent->dispose();
+
+ // release references
+ mpWakeupEvent.reset();
+
+ ActivityBase::dispose();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/discreteactivitybase.hxx b/slideshow/source/engine/activities/discreteactivitybase.hxx
new file mode 100644
index 000000000..1332ad3c5
--- /dev/null
+++ b/slideshow/source/engine/activities/discreteactivitybase.hxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_DISCRETEACTIVITYBASE_HXX
+#define INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_DISCRETEACTIVITYBASE_HXX
+
+#include "activitybase.hxx"
+#include <wakeupevent.hxx>
+
+#include <vector>
+
+
+namespace slideshow::internal
+ {
+ /** Specialization of ActivityBase for discrete time activities.
+
+ A discrete time activity is one where time proceeds in
+ discrete steps, i.e. at given time instants.
+ */
+ class DiscreteActivityBase : public ActivityBase
+ {
+ public:
+ explicit DiscreteActivityBase( const ActivityParameters& rParms );
+
+ /** Hook for derived classes.
+
+ This method is called for each discrete time
+ instant, with nFrame denoting the frame number
+ (starting with 0)
+
+ @param nFrame
+ Current frame number.
+
+ @param nRepeatCount
+ Number of full repeats already performed
+ */
+ virtual void perform( sal_uInt32 nFrame, sal_uInt32 nRepeatCount ) const = 0;
+ virtual void dispose() override;
+ virtual bool perform() override;
+
+ protected:
+ virtual void startAnimation() override;
+
+ sal_uInt32 calcFrameIndex( sal_uInt32 nCurrCalls,
+ ::std::size_t nVectorSize ) const;
+
+ sal_uInt32 calcRepeatCount( sal_uInt32 nCurrCalls,
+ ::std::size_t nVectorSize ) const;
+
+ ::std::size_t getNumberOfKeyTimes() const { return maDiscreteTimes.size(); }
+
+ private:
+ WakeupEventSharedPtr mpWakeupEvent;
+ const ::std::vector< double > maDiscreteTimes;
+ const double mnSimpleDuration;
+ sal_uInt32 mnCurrPerformCalls;
+ };
+
+}
+
+#endif // INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_DISCRETEACTIVITYBASE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/interpolation.hxx b/slideshow/source/engine/activities/interpolation.hxx
new file mode 100644
index 000000000..155456ed5
--- /dev/null
+++ b/slideshow/source/engine/activities/interpolation.hxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_INTERPOLATION_HXX
+#define INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_INTERPOLATION_HXX
+
+#include <basegfx/utils/lerp.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+
+#include <rgbcolor.hxx>
+#include <hslcolor.hxx>
+
+namespace basegfx
+{
+ namespace utils
+ {
+ // Interpolator specializations
+ // ============================
+
+ // NOTE: generic lerp is included from lerp.hxx. Following
+ // are some specializations for various
+ // not-straight-forward-interpolatable types
+
+ /// Specialization for RGBColor, to employ color-specific interpolator
+ template<> ::slideshow::internal::RGBColor lerp< ::slideshow::internal::RGBColor >(
+ const ::slideshow::internal::RGBColor& rFrom,
+ const ::slideshow::internal::RGBColor& rTo,
+ double t )
+ {
+ return interpolate( rFrom, rTo, t );
+ }
+
+ /// Specialization also for sal_Int16, although this code should not be called
+ template<> sal_Int16 lerp< sal_Int16 >( const sal_Int16&,
+ const sal_Int16& rTo,
+ double )
+ {
+ OSL_FAIL( "lerp<sal_Int16> called" );
+ return rTo;
+ }
+
+ /// Specialization also for string, although this code should not be called
+ template<> OUString lerp< OUString >( const OUString&,
+ const OUString& rTo,
+ double )
+ {
+ OSL_FAIL( "lerp<OUString> called" );
+ return rTo;
+ }
+
+ /// Specialization also for bool, although this code should not be called
+ template<> bool lerp< bool >( const bool&,
+ const bool& rTo,
+ double )
+ {
+ OSL_FAIL( "lerp<bool> called" );
+ return rTo;
+ }
+ }
+}
+
+namespace slideshow
+{
+ namespace internal
+ {
+ template< typename ValueType > struct Interpolator
+ {
+ ValueType operator()( const ValueType& rFrom,
+ const ValueType& rTo,
+ double t ) const
+ {
+ return basegfx::utils::lerp( rFrom, rTo, t );
+ }
+ };
+
+ /// Specialization for HSLColor, to employ color-specific interpolator
+ template<> struct Interpolator< HSLColor >
+ {
+ explicit Interpolator( bool bCCW ) :
+ mbCCW( bCCW )
+ {
+ }
+
+ HSLColor operator()( const HSLColor& rFrom,
+ const HSLColor& rTo,
+ double t ) const
+ {
+ return interpolate( rFrom, rTo, t, mbCCW );
+ }
+
+ private:
+ /// When true: interpolate counter-clockwise
+ const bool mbCCW;
+ };
+
+
+ /** Generic linear interpolator
+
+ @tpl ValueType
+ Must have operator+ and operator* defined, and should
+ have value semantics.
+
+ @param rInterpolator
+ Interpolator to use for lerp
+
+ @param nFrame
+ Must be in the [0,nTotalFrames) range
+
+ @param nTotalFrames
+ Total number of frames. Should be greater than zero.
+ */
+ template< typename ValueType > ValueType lerp( const Interpolator< ValueType >& rInterpolator,
+ const ValueType& rFrom,
+ const ValueType& rTo,
+ sal_uInt32 nFrame,
+ ::std::size_t nTotalFrames )
+ {
+ // TODO(P1): There's a nice HAKMEM trick for that
+ // nTotalFrames > 1 condition below
+
+ // for 1 and 0 frame animations, always take end value
+ const double nFraction( nTotalFrames > 1 ? double(nFrame)/(nTotalFrames-1) : 1.0 );
+
+ return rInterpolator( rFrom, rTo, nFraction );
+ }
+
+ /// Specialization for non-interpolatable constants/enums
+ template<> sal_Int16 lerp< sal_Int16 >( const Interpolator< sal_Int16 >& /*rInterpolator*/,
+ const sal_Int16& rFrom,
+ const sal_Int16& rTo,
+ sal_uInt32 nFrame,
+ ::std::size_t nTotalFrames )
+ {
+ // until one half of the total frames are over, take from value.
+ // after that, take to value.
+ // For nFrames not divisible by 2, we prefer to over from, which
+ // also neatly yields to for 1 frame activities
+ return nFrame < nTotalFrames/2 ? rFrom : rTo;
+ }
+
+ /// Specialization for non-interpolatable strings
+ template<> OUString lerp< OUString >( const Interpolator< OUString >& /*rInterpolator*/,
+ const OUString& rFrom,
+ const OUString& rTo,
+ sal_uInt32 nFrame,
+ ::std::size_t nTotalFrames )
+ {
+ // until one half of the total frames are over, take from value.
+ // after that, take to value.
+ // For nFrames not divisible by 2, we prefer to over from, which
+ // also neatly yields to for 1 frame activities
+ return nFrame < nTotalFrames/2 ? rFrom : rTo;
+ }
+
+ /// Specialization for non-interpolatable bools
+ template<> bool lerp< bool >( const Interpolator< bool >& /*rInterpolator*/,
+ const bool& bFrom,
+ const bool& bTo,
+ sal_uInt32 nFrame,
+ ::std::size_t nTotalFrames )
+ {
+ // until one half of the total frames are over, take from value.
+ // after that, take to value.
+ // For nFrames not divisible by 2, we prefer to over from, which
+ // also neatly yields to for 1 frame activities
+ return nFrame < nTotalFrames/2 ? bFrom : bTo;
+ }
+ }
+}
+
+#endif // INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_INTERPOLATION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/simplecontinuousactivitybase.cxx b/slideshow/source/engine/activities/simplecontinuousactivitybase.cxx
new file mode 100644
index 000000000..01cb3b750
--- /dev/null
+++ b/slideshow/source/engine/activities/simplecontinuousactivitybase.cxx
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+// must be first
+
+#include "simplecontinuousactivitybase.hxx"
+
+#include <sal/log.hxx>
+
+namespace slideshow::internal
+{
+ SimpleContinuousActivityBase::SimpleContinuousActivityBase(
+ const ActivityParameters& rParms ) :
+ ActivityBase( rParms ),
+ maTimer( rParms.mrActivitiesQueue.getTimer() ),
+ mnMinSimpleDuration( rParms.mnMinDuration ),
+ mnMinNumberOfFrames( rParms.mnMinNumberOfFrames ),
+ mnCurrPerformCalls( 0 )
+ {
+ }
+
+ void SimpleContinuousActivityBase::startAnimation()
+ {
+ // init timer. We measure animation time only when we're
+ // actually started.
+ maTimer.reset();
+ }
+
+ double SimpleContinuousActivityBase::calcTimeLag() const
+ {
+ ActivityBase::calcTimeLag();
+ if (! isActive())
+ return 0.0;
+
+ // retrieve locally elapsed time
+ const double nCurrElapsedTime( maTimer.getElapsedTime() );
+
+ // log time
+ SAL_INFO("slideshow.verbose", "SimpleContinuousActivityBase::calcTimeLag(): "
+ "next step is based on time: " << nCurrElapsedTime );
+
+ // go to great length to ensure a proper animation
+ // run. Since we don't know how often we will be called
+ // here, try to spread the animator calls uniquely over
+ // the [0,1] parameter range. Be aware of the fact that
+ // perform will be called at least mnMinNumberOfTurns
+ // times.
+
+ // fraction of time elapsed (clamp to 1.0 for zero-length
+ // animations)
+ const double nFractionElapsedTime(
+ mnMinSimpleDuration != 0.0 ?
+ nCurrElapsedTime / mnMinSimpleDuration :
+ 1.0 );
+
+ // fraction of minimum calls performed
+ const double nFractionRequiredCalls(
+ double(mnCurrPerformCalls) / mnMinNumberOfFrames );
+
+ // okay, so now, the decision is easy:
+
+ // If the fraction of time elapsed is smaller than the
+ // number of calls required to be performed, then we calc
+ // the position on the animation range according to
+ // elapsed time. That is, we're so to say ahead of time.
+
+ // In contrary, if the fraction of time elapsed is larger,
+ // then we're lagging, and we thus calc the position on
+ // the animation time line according to the fraction of
+ // calls performed. Thus, the animation is forced to slow
+ // down, and take the required minimal number of steps,
+ // sufficiently equally distributed across the animation
+ // time line.
+ if( nFractionElapsedTime < nFractionRequiredCalls )
+ {
+ SAL_INFO("slideshow.verbose", "SimpleContinuousActivityBase::calcTimeLag(): t=" <<
+ nFractionElapsedTime <<
+ " is based on time");
+ return 0.0;
+ }
+ else
+ {
+ SAL_INFO("slideshow.verbose", "SimpleContinuousActivityBase::perform(): t=" <<
+ nFractionRequiredCalls <<
+ " is based on number of calls");
+
+ // lag global time, so all other animations lag, too:
+ return ((nFractionElapsedTime - nFractionRequiredCalls)
+ * mnMinSimpleDuration);
+ }
+ }
+
+ bool SimpleContinuousActivityBase::perform()
+ {
+ // call base class, for start() calls and end handling
+ if( !ActivityBase::perform() )
+ return false; // done, we're ended
+
+
+ // get relative animation position
+ // ===============================
+
+ const double nCurrElapsedTime( maTimer.getElapsedTime() );
+ // clamp to 1.0 for zero animation duration
+ double nT( mnMinSimpleDuration != 0.0 ?
+ nCurrElapsedTime / mnMinSimpleDuration :
+ 1.0 );
+
+
+ // one of the stop criteria reached?
+ // =================================
+
+ // will be set to true below, if one of the termination criteria
+ // matched.
+ bool bActivityEnding( false );
+
+ if( isRepeatCountValid() )
+ {
+ // Finite duration
+ // ===============
+
+ // When we've autoreverse on, the repeat count
+ // doubles
+ const double nRepeatCount( getRepeatCount() );
+ const double nEffectiveRepeat( isAutoReverse() ?
+ 2.0*nRepeatCount :
+ nRepeatCount );
+
+ // time (or frame count) elapsed?
+ if( nEffectiveRepeat <= nT )
+ {
+ // okee. done for now. Will not exit right here,
+ // to give animation the chance to render the last
+ // frame below
+ bActivityEnding = true;
+
+ // clamp animation to max permissible value
+ nT = nEffectiveRepeat;
+ }
+ }
+
+
+ // need to do auto-reverse?
+ // ========================
+
+ double nRepeats;
+ double nRelativeSimpleTime;
+
+ // TODO(Q3): Refactor this mess
+ if( isAutoReverse() )
+ {
+ // divert active duration into repeat and
+ // fractional part.
+ const double nFractionalActiveDuration( modf(nT, &nRepeats) );
+
+ // for auto-reverse, map ranges [1,2), [3,4), ...
+ // to ranges [0,1), [1,2), etc.
+ if( static_cast<int>(nRepeats) % 2 )
+ {
+ // we're in an odd range, reverse sweep
+ nRelativeSimpleTime = 1.0 - nFractionalActiveDuration;
+ }
+ else
+ {
+ // we're in an even range, pass on as is
+ nRelativeSimpleTime = nFractionalActiveDuration;
+ }
+
+ // effective repeat count for autoreverse is half of
+ // the input time's value (each run of an autoreverse
+ // cycle is half of a repeat)
+ nRepeats /= 2;
+ }
+ else
+ {
+ // determine repeat
+ // ================
+
+ // calc simple time and number of repeats from nT
+ // Now, that's easy, since the fractional part of
+ // nT gives the relative simple time, and the
+ // integer part the number of full repeats:
+ nRelativeSimpleTime = modf(nT, &nRepeats);
+
+ // clamp repeats to max permissible value (maRepeats.getValue() - 1.0)
+ if( isRepeatCountValid() &&
+ nRepeats >= getRepeatCount() )
+ {
+ // Note that this code here only gets
+ // triggered if maRepeats.getValue() is an
+ // _integer_. Otherwise, nRepeats will never
+ // reach nor exceed
+ // maRepeats.getValue(). Thus, the code below
+ // does not need to handle cases of fractional
+ // repeats, and can always assume that a full
+ // animation run has ended (with
+ // nRelativeSimpleTime=1.0 for
+ // non-autoreversed activities).
+
+ // with modf, nRelativeSimpleTime will never
+ // become 1.0, since nRepeats is incremented and
+ // nRelativeSimpleTime set to 0.0 then.
+
+ // For the animation to reach its final value,
+ // nRepeats must although become
+ // maRepeats.getValue()-1.0, and
+ // nRelativeSimpleTime=1.0.
+ nRelativeSimpleTime = 1.0;
+ nRepeats -= 1.0;
+ }
+ }
+
+ // actually perform something
+ // ==========================
+
+ simplePerform( nRelativeSimpleTime,
+ // nRepeats is already integer-valued
+ static_cast<sal_uInt32>( nRepeats ) );
+
+
+ // delayed endActivity() call from end condition check
+ // below. Issued after the simplePerform() call above, to
+ // give animations the chance to correctly reach the
+ // animation end value, without spurious bail-outs because
+ // of isActive() returning false.
+ if( bActivityEnding )
+ endActivity();
+
+ // one more frame successfully performed
+ ++mnCurrPerformCalls;
+
+ return isActive();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/slideshow/source/engine/activities/simplecontinuousactivitybase.hxx b/slideshow/source/engine/activities/simplecontinuousactivitybase.hxx
new file mode 100644
index 000000000..fb4f74605
--- /dev/null
+++ b/slideshow/source/engine/activities/simplecontinuousactivitybase.hxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_SIMPLECONTINUOUSACTIVITYBASE_HXX
+#define INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_SIMPLECONTINUOUSACTIVITYBASE_HXX
+
+#include "activitybase.hxx"
+#include <canvas/elapsedtime.hxx>
+
+namespace slideshow::internal
+ {
+ /** Simple, continuous animation.
+
+ This class implements a simple, continuous animation
+ without considering repeats or acceleration on the
+ perform call. Only useful as a base class, you
+ probably want to use ContinuousActivityBase.
+ */
+ class SimpleContinuousActivityBase : public ActivityBase
+ {
+ public:
+ explicit SimpleContinuousActivityBase( const ActivityParameters& rParms );
+
+ virtual double calcTimeLag() const override;
+ virtual bool perform() override;
+
+ protected:
+ /** Hook for derived classes
+
+ This method will be called from perform().
+
+ @param nSimpleTime
+ Simple animation time, without repeat,
+ acceleration or deceleration applied. This value
+ is always in the [0,1] range, the repeat is
+ accounted for with the nRepeatCount parameter.
+
+ @param nRepeatCount
+ Number of full repeats already performed
+ */
+ virtual void simplePerform( double nSimpleTime, sal_uInt32 nRepeatCount ) const = 0;
+
+ virtual void startAnimation() override;
+
+ private:
+ /// Time elapsed since activity started
+ ::canvas::tools::ElapsedTime maTimer;
+
+ /// Simple duration of activity
+ const double mnMinSimpleDuration;
+
+ /// Minimal number of frames to show (see ActivityParameters)
+ const sal_uInt32 mnMinNumberOfFrames;
+
+ /// Actual number of frames shown until now.
+ sal_uInt32 mnCurrPerformCalls;
+ };
+
+}
+
+#endif // INCLUDED_SLIDESHOW_SOURCE_ENGINE_ACTIVITIES_SIMPLECONTINUOUSACTIVITYBASE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */