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