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 <discreteactivitybase.hxx>
33 
34 
35 namespace slideshow
36 {
37     namespace internal
38     {
DiscreteActivityBase(const ActivityParameters & rParms)39         DiscreteActivityBase::DiscreteActivityBase( const ActivityParameters& rParms ) :
40             ActivityBase( rParms ),
41             mpWakeupEvent( rParms.mpWakeupEvent ),
42             maDiscreteTimes( rParms.maDiscreteTimes ),
43             mnSimpleDuration( rParms.mnMinDuration ),
44             mnCurrPerformCalls( 0 )
45         {
46             ENSURE_OR_THROW( mpWakeupEvent,
47                               "DiscreteActivityBase::DiscreteActivityBase(): Invalid wakeup event" );
48 
49             ENSURE_OR_THROW( !maDiscreteTimes.empty(),
50                               "DiscreteActivityBase::DiscreteActivityBase(): time vector is empty, why do you create me?" );
51 
52 #ifdef DBG_UTIL
53             // check parameters: rDiscreteTimes must be sorted in
54             // ascending order, and contain values only from the range
55             // [0,1]
56             for( ::std::size_t i=1, len=maDiscreteTimes.size(); i<len; ++i )
57             {
58                 if( maDiscreteTimes[i] < 0.0 ||
59                     maDiscreteTimes[i] > 1.0 ||
60                     maDiscreteTimes[i-1] < 0.0 ||
61                     maDiscreteTimes[i-1] > 1.0 )
62                 {
63                     ENSURE_OR_THROW( false, "DiscreteActivityBase::DiscreteActivityBase(): time values not within [0,1] range!" );
64                 }
65 
66                 if( maDiscreteTimes[i-1] > maDiscreteTimes[i] )
67                     ENSURE_OR_THROW( false, "DiscreteActivityBase::DiscreteActivityBase(): time vector is not sorted in ascending order!" );
68             }
69 
70             // TODO(E2): check this also in production code?
71 #endif
72         }
73 
startAnimation()74         void DiscreteActivityBase::startAnimation()
75         {
76             // start timer on wakeup event
77             mpWakeupEvent->start();
78         }
79 
calcFrameIndex(sal_uInt32 nCurrCalls,::std::size_t nVectorSize) const80         sal_uInt32 DiscreteActivityBase::calcFrameIndex( sal_uInt32 	nCurrCalls,
81                                                          ::std::size_t 	nVectorSize ) const
82         {
83             if( isAutoReverse() )
84             {
85                 // every full repeat run consists of one
86                 // forward and one backward traversal.
87                 sal_uInt32 nFrameIndex( nCurrCalls % (2*nVectorSize) );
88 
89                 // nFrameIndex values >= nVectorSize belong to
90                 // the backward traversal
91                 if( nFrameIndex >= nVectorSize )
92                     nFrameIndex = 2*nVectorSize - nFrameIndex; // invert sweep
93 
94                 return nFrameIndex;
95             }
96             else
97             {
98                 return nCurrCalls % nVectorSize ;
99             }
100         }
101 
calcRepeatCount(sal_uInt32 nCurrCalls,::std::size_t nVectorSize) const102         sal_uInt32 DiscreteActivityBase::calcRepeatCount( sal_uInt32 	nCurrCalls,
103                                                           ::std::size_t	nVectorSize ) const
104         {
105             if( isAutoReverse() )
106                 return nCurrCalls / (2*nVectorSize); // we've got 2 cycles per repeat
107             else
108                 return nCurrCalls / nVectorSize;
109         }
110 
perform()111         bool DiscreteActivityBase::perform()
112         {
113             // call base class, for start() calls and end handling
114             if( !ActivityBase::perform() )
115                 return false; // done, we're ended
116 
117             const ::std::size_t nVectorSize( maDiscreteTimes.size() );
118 
119             // actually perform something
120             // ==========================
121 
122             // TODO(Q3): Refactor this mess
123 
124             // call derived class with current frame index (modulo
125             // vector size, to cope with repeats)
126             perform( calcFrameIndex( mnCurrPerformCalls, nVectorSize ),
127                      calcRepeatCount( mnCurrPerformCalls, nVectorSize ) );
128 
129             // calc next index
130             ++mnCurrPerformCalls;
131 
132             // calc currently reached repeat count
133             double nCurrRepeat( double(mnCurrPerformCalls) / nVectorSize );
134 
135             // if auto-reverse is specified, halve the
136             // effective repeat count, since we pass every
137             // repeat run twice: once forward, once backward.
138             if( isAutoReverse() )
139                 nCurrRepeat /= 2.0;
140 
141             // schedule next frame, if either repeat is indefinite
142             // (repeat forever), or we've not yet reached the requested
143             // repeat count
144             if( !isRepeatCountValid() ||
145                 nCurrRepeat < getRepeatCount() )
146             {
147                 // add wake-up event to queue (modulo
148                 // vector size, to cope with repeats).
149 
150                 // repeat is handled locally, only apply acceleration/deceleration.
151                 // Scale time vector with simple duration, offset with full repeat
152                 // times.
153                 //
154                 // Somewhat condensed, the argument for setNextTimeout below could
155                 // be written as
156                 //
157                 // mnSimpleDuration*(nFullRepeats + calcAcceleratedTime( currentRepeatTime )),
158                 //
159                 // with currentRepeatTime = maDiscreteTimes[ currentRepeatIndex ]
160                 //
161                 // Note that calcAcceleratedTime() is only applied to the current repeat's value,
162                 // not to the total resulting time. This is in accordance with the SMIL spec.
163                 //
164                 mpWakeupEvent->setNextTimeout(
165                     mnSimpleDuration*(
166                         calcRepeatCount(
167                             mnCurrPerformCalls,
168                             nVectorSize ) +
169                         calcAcceleratedTime(
170                             maDiscreteTimes[
171                                 calcFrameIndex(
172                                     mnCurrPerformCalls,
173                                     nVectorSize ) ] ) ) );
174 
175                 getEventQueue().addEvent( mpWakeupEvent );
176             }
177             else
178             {
179                 // release event reference (relation to wakeup event
180                 // is circular!)
181                 mpWakeupEvent.reset();
182 
183                 // done with this activity
184                 endActivity();
185             }
186 
187             return false; // remove from queue, will be added back by the wakeup event.
188         }
189 
dispose()190         void DiscreteActivityBase::dispose()
191         {
192             // dispose event
193             if( mpWakeupEvent )
194                 mpWakeupEvent->dispose();
195 
196             // release references
197             mpWakeupEvent.reset();
198 
199             ActivityBase::dispose();
200         }
201     }
202 }
203