| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) | |
| 3 | |
| 4 This library is free software; you can redistribute it and/or | |
| 5 modify it under the terms of the GNU Library General Public | |
| 6 License as published by the Free Software Foundation; either | |
| 7 version 2 of the License, or (at your option) any later version. | |
| 8 | |
| 9 This library is distributed in the hope that it will be useful, | |
| 10 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 12 Library General Public License for more details. | |
| 13 | |
| 14 You should have received a copy of the GNU Library General Public License | |
| 15 along with this library; see the file COPYING.LIB. If not, write to | |
| 16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
| 17 Boston, MA 02110-1301, USA. | |
| 18 */ | |
| 19 | |
| 20 #include "config.h" | |
| 21 | |
| 22 #include "core/platform/graphics/GraphicsLayerAnimation.h" | |
| 23 | |
| 24 #include "core/platform/graphics/LayoutSize.h" | |
| 25 #include "core/platform/graphics/UnitBezier.h" | |
| 26 #include <wtf/CurrentTime.h> | |
| 27 | |
| 28 namespace WebCore { | |
| 29 | |
| 30 static inline PassRefPtr<FilterOperation> blendFunc(FilterOperation* fromOp, Fil
terOperation* toOp, double progress, const IntSize& size, bool blendToPassthroug
h = false) | |
| 31 { | |
| 32 ASSERT(toOp); | |
| 33 if (toOp->blendingNeedsRendererSize()) | |
| 34 return toOp->blend(fromOp, progress, LayoutSize(size.width(), size.heigh
t()), blendToPassthrough); | |
| 35 | |
| 36 return toOp->blend(fromOp, progress, blendToPassthrough); | |
| 37 } | |
| 38 | |
| 39 | |
| 40 static FilterOperations applyFilterAnimation(const FilterOperations* from, const
FilterOperations* to, double progress, const IntSize& boxSize) | |
| 41 { | |
| 42 // First frame of an animation. | |
| 43 if (!progress) | |
| 44 return *from; | |
| 45 | |
| 46 // Last frame of an animation. | |
| 47 if (progress == 1) | |
| 48 return *to; | |
| 49 | |
| 50 if (!from->operationsMatch(*to)) | |
| 51 return *to; | |
| 52 | |
| 53 FilterOperations result; | |
| 54 | |
| 55 size_t fromSize = from->operations().size(); | |
| 56 size_t toSize = to->operations().size(); | |
| 57 size_t size = std::max(fromSize, toSize); | |
| 58 for (size_t i = 0; i < size; i++) { | |
| 59 RefPtr<FilterOperation> fromOp = (i < fromSize) ? from->operations()[i].
get() : 0; | |
| 60 RefPtr<FilterOperation> toOp = (i < toSize) ? to->operations()[i].get()
: 0; | |
| 61 RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(fromOp.get(), toOp.
get(), progress, boxSize) : (fromOp ? blendFunc(0, fromOp.get(), progress, boxSi
ze, true) : 0); | |
| 62 if (blendedOp) | |
| 63 result.operations().append(blendedOp); | |
| 64 else { | |
| 65 RefPtr<FilterOperation> identityOp = PassthroughFilterOperation::cre
ate(); | |
| 66 if (progress > 0.5) | |
| 67 result.operations().append(toOp ? toOp : identityOp); | |
| 68 else | |
| 69 result.operations().append(fromOp ? fromOp : identityOp); | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 return result; | |
| 74 } | |
| 75 | |
| 76 static bool shouldReverseAnimationValue(CSSAnimationData::AnimationDirection dir
ection, int loopCount) | |
| 77 { | |
| 78 if (((direction == CSSAnimationData::AnimationDirectionAlternate) && (loopCo
unt & 1)) | |
| 79 || ((direction == CSSAnimationData::AnimationDirectionAlternateReverse)
&& !(loopCount & 1)) | |
| 80 || direction == CSSAnimationData::AnimationDirectionReverse) | |
| 81 return true; | |
| 82 return false; | |
| 83 } | |
| 84 | |
| 85 static double normalizedAnimationValue(double runningTime, double duration, CSSA
nimationData::AnimationDirection direction, double iterationCount) | |
| 86 { | |
| 87 if (!duration) | |
| 88 return 0; | |
| 89 | |
| 90 const int loopCount = runningTime / duration; | |
| 91 const double lastFullLoop = duration * double(loopCount); | |
| 92 const double remainder = runningTime - lastFullLoop; | |
| 93 // Ignore remainder when we've reached the end of animation. | |
| 94 const double normalized = (loopCount == iterationCount) ? 1.0 : (remainder /
duration); | |
| 95 | |
| 96 return shouldReverseAnimationValue(direction, loopCount) ? 1 - normalized :
normalized; | |
| 97 } | |
| 98 | |
| 99 static double normalizedAnimationValueForFillsForwards(double iterationCount, CS
SAnimationData::AnimationDirection direction) | |
| 100 { | |
| 101 if (direction == CSSAnimationData::AnimationDirectionNormal) | |
| 102 return 1; | |
| 103 if (direction == CSSAnimationData::AnimationDirectionReverse) | |
| 104 return 0; | |
| 105 return shouldReverseAnimationValue(direction, iterationCount) ? 1 : 0; | |
| 106 } | |
| 107 | |
| 108 static float applyOpacityAnimation(float fromOpacity, float toOpacity, double pr
ogress) | |
| 109 { | |
| 110 // Optimization: special case the edge values (0 and 1). | |
| 111 if (progress == 1.0) | |
| 112 return toOpacity; | |
| 113 | |
| 114 if (!progress) | |
| 115 return fromOpacity; | |
| 116 | |
| 117 return fromOpacity + progress * (toOpacity - fromOpacity); | |
| 118 } | |
| 119 | |
| 120 static inline double solveEpsilon(double duration) | |
| 121 { | |
| 122 return 1.0 / (200.0 * duration); | |
| 123 } | |
| 124 | |
| 125 static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x
, double p2y, double t, double duration) | |
| 126 { | |
| 127 return UnitBezier(p1x, p1y, p2x, p2y).solve(t, solveEpsilon(duration)); | |
| 128 } | |
| 129 | |
| 130 static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t
) | |
| 131 { | |
| 132 if (stepAtStart) | |
| 133 return std::min(1.0, (floor(numSteps * t) + 1) / numSteps); | |
| 134 return floor(numSteps * t) / numSteps; | |
| 135 } | |
| 136 | |
| 137 static inline float applyTimingFunction(const TimingFunction* timingFunction, fl
oat progress, double duration) | |
| 138 { | |
| 139 if (!timingFunction) | |
| 140 return progress; | |
| 141 | |
| 142 if (timingFunction->isCubicBezierTimingFunction()) { | |
| 143 const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimi
ngFunction*>(timingFunction); | |
| 144 return solveCubicBezierFunction(ctf->x1(), ctf->y1(), ctf->x2(), ctf->y2
(), progress, duration); | |
| 145 } | |
| 146 | |
| 147 if (timingFunction->isStepsTimingFunction()) { | |
| 148 const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>
(timingFunction); | |
| 149 return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), doub
le(progress)); | |
| 150 } | |
| 151 | |
| 152 return progress; | |
| 153 } | |
| 154 | |
| 155 static TransformationMatrix applyTransformAnimation(const TransformOperations* f
rom, const TransformOperations* to, double progress, const IntSize& boxSize, boo
l listsMatch) | |
| 156 { | |
| 157 TransformationMatrix matrix; | |
| 158 | |
| 159 // First frame of an animation. | |
| 160 if (!progress) { | |
| 161 from->apply(boxSize, matrix); | |
| 162 return matrix; | |
| 163 } | |
| 164 | |
| 165 // Last frame of an animation. | |
| 166 if (progress == 1) { | |
| 167 to->apply(boxSize, matrix); | |
| 168 return matrix; | |
| 169 } | |
| 170 | |
| 171 // If we have incompatible operation lists, we blend the resulting matrices. | |
| 172 if (!listsMatch) { | |
| 173 TransformationMatrix fromMatrix; | |
| 174 to->apply(boxSize, matrix); | |
| 175 from->apply(boxSize, fromMatrix); | |
| 176 matrix.blend(fromMatrix, progress); | |
| 177 return matrix; | |
| 178 } | |
| 179 | |
| 180 // Animation to "-webkit-transform: none". | |
| 181 if (!to->size()) { | |
| 182 TransformOperations blended(*from); | |
| 183 for (size_t i = 0; i < blended.operations().size(); ++i) | |
| 184 blended.operations()[i]->blend(0, progress, true)->apply(matrix, box
Size); | |
| 185 return matrix; | |
| 186 } | |
| 187 | |
| 188 // Animation from "-webkit-transform: none". | |
| 189 if (!from->size()) { | |
| 190 TransformOperations blended(*to); | |
| 191 for (size_t i = 0; i < blended.operations().size(); ++i) | |
| 192 blended.operations()[i]->blend(0, 1. - progress, true)->apply(matrix
, boxSize); | |
| 193 return matrix; | |
| 194 } | |
| 195 | |
| 196 // Normal animation with a matching operation list. | |
| 197 TransformOperations blended(*to); | |
| 198 for (size_t i = 0; i < blended.operations().size(); ++i) | |
| 199 blended.operations()[i]->blend(from->at(i), progress, !from->at(i))->app
ly(matrix, boxSize); | |
| 200 return matrix; | |
| 201 } | |
| 202 | |
| 203 static const TimingFunction* timingFunctionForAnimationValue(const AnimationValu
e* animValue, const CSSAnimationData* anim) | |
| 204 { | |
| 205 if (animValue->timingFunction()) | |
| 206 return animValue->timingFunction(); | |
| 207 if (anim->timingFunction()) | |
| 208 return anim->timingFunction().get(); | |
| 209 | |
| 210 return CubicBezierTimingFunction::defaultTimingFunction(); | |
| 211 } | |
| 212 | |
| 213 GraphicsLayerAnimation::GraphicsLayerAnimation(const String& name, const Keyfram
eValueList& keyframes, const IntSize& boxSize, const CSSAnimationData* animation
, double startTime, bool listsMatch) | |
| 214 : m_keyframes(keyframes) | |
| 215 , m_boxSize(boxSize) | |
| 216 , m_animation(CSSAnimationData::create(animation)) | |
| 217 , m_name(name) | |
| 218 , m_listsMatch(listsMatch) | |
| 219 , m_startTime(startTime) | |
| 220 , m_pauseTime(0) | |
| 221 , m_totalRunningTime(0) | |
| 222 , m_lastRefreshedTime(m_startTime) | |
| 223 , m_state(PlayingState) | |
| 224 { | |
| 225 } | |
| 226 | |
| 227 void GraphicsLayerAnimation::applyInternal(Client* client, const AnimationValue*
from, const AnimationValue* to, float progress) | |
| 228 { | |
| 229 switch (m_keyframes.property()) { | |
| 230 case AnimatedPropertyOpacity: | |
| 231 client->setAnimatedOpacity(applyOpacityAnimation((static_cast<const Floa
tAnimationValue*>(from)->value()), (static_cast<const FloatAnimationValue*>(to)-
>value()), progress)); | |
| 232 return; | |
| 233 case AnimatedPropertyWebkitTransform: | |
| 234 client->setAnimatedTransform(applyTransformAnimation(static_cast<const T
ransformAnimationValue*>(from)->value(), static_cast<const TransformAnimationVal
ue*>(to)->value(), progress, m_boxSize, m_listsMatch)); | |
| 235 return; | |
| 236 case AnimatedPropertyWebkitFilter: | |
| 237 client->setAnimatedFilters(applyFilterAnimation(static_cast<const Filter
AnimationValue*>(from)->value(), static_cast<const FilterAnimationValue*>(to)->v
alue(), progress, m_boxSize)); | |
| 238 return; | |
| 239 default: | |
| 240 ASSERT_NOT_REACHED(); | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 bool GraphicsLayerAnimation::isActive() const | |
| 245 { | |
| 246 if (state() != StoppedState) | |
| 247 return true; | |
| 248 | |
| 249 return m_animation->fillsForwards(); | |
| 250 } | |
| 251 | |
| 252 bool GraphicsLayerAnimations::hasActiveAnimationsOfType(AnimatedPropertyID type)
const | |
| 253 { | |
| 254 for (size_t i = 0; i < m_animations.size(); ++i) { | |
| 255 if (m_animations[i].isActive() && m_animations[i].property() == type) | |
| 256 return true; | |
| 257 } | |
| 258 return false; | |
| 259 } | |
| 260 | |
| 261 bool GraphicsLayerAnimations::hasRunningAnimations() const | |
| 262 { | |
| 263 for (size_t i = 0; i < m_animations.size(); ++i) { | |
| 264 if (m_animations[i].state() == GraphicsLayerAnimation::PlayingState) | |
| 265 return true; | |
| 266 } | |
| 267 | |
| 268 return false; | |
| 269 } | |
| 270 | |
| 271 void GraphicsLayerAnimation::apply(Client* client) | |
| 272 { | |
| 273 if (!isActive()) | |
| 274 return; | |
| 275 | |
| 276 double totalRunningTime = computeTotalRunningTime(); | |
| 277 double normalizedValue = normalizedAnimationValue(totalRunningTime, m_animat
ion->duration(), m_animation->direction(), m_animation->iterationCount()); | |
| 278 | |
| 279 if (m_animation->iterationCount() != CSSAnimationData::IterationCountInfinit
e && totalRunningTime >= m_animation->duration() * m_animation->iterationCount()
) { | |
| 280 setState(StoppedState); | |
| 281 if (m_animation->fillsForwards()) | |
| 282 normalizedValue = normalizedAnimationValueForFillsForwards(m_animati
on->iterationCount(), m_animation->direction()); | |
| 283 } | |
| 284 | |
| 285 if (!normalizedValue) { | |
| 286 applyInternal(client, m_keyframes.at(0), m_keyframes.at(1), 0); | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 if (normalizedValue == 1.0) { | |
| 291 applyInternal(client, m_keyframes.at(m_keyframes.size() - 2), m_keyframe
s.at(m_keyframes.size() - 1), 1); | |
| 292 return; | |
| 293 } | |
| 294 if (m_keyframes.size() == 2) { | |
| 295 const TimingFunction* timingFunction = timingFunctionForAnimationValue(m
_keyframes.at(0), m_animation.get()); | |
| 296 normalizedValue = applyTimingFunction(timingFunction, normalizedValue, m
_animation->duration()); | |
| 297 applyInternal(client, m_keyframes.at(0), m_keyframes.at(1), normalizedVa
lue); | |
| 298 return; | |
| 299 } | |
| 300 | |
| 301 for (size_t i = 0; i < m_keyframes.size() - 1; ++i) { | |
| 302 const AnimationValue* from = m_keyframes.at(i); | |
| 303 const AnimationValue* to = m_keyframes.at(i + 1); | |
| 304 if (from->keyTime() > normalizedValue || to->keyTime() < normalizedValue
) | |
| 305 continue; | |
| 306 | |
| 307 normalizedValue = (normalizedValue - from->keyTime()) / (to->keyTime() -
from->keyTime()); | |
| 308 const TimingFunction* timingFunction = timingFunctionForAnimationValue(f
rom, m_animation.get()); | |
| 309 normalizedValue = applyTimingFunction(timingFunction, normalizedValue, m
_animation->duration()); | |
| 310 applyInternal(client, from, to, normalizedValue); | |
| 311 break; | |
| 312 } | |
| 313 } | |
| 314 | |
| 315 double GraphicsLayerAnimation::computeTotalRunningTime() | |
| 316 { | |
| 317 if (state() == PausedState) | |
| 318 return m_pauseTime; | |
| 319 | |
| 320 double oldLastRefreshedTime = m_lastRefreshedTime; | |
| 321 m_lastRefreshedTime = WTF::currentTime(); | |
| 322 m_totalRunningTime += m_lastRefreshedTime - oldLastRefreshedTime; | |
| 323 return m_totalRunningTime; | |
| 324 } | |
| 325 | |
| 326 void GraphicsLayerAnimation::pause(double time) | |
| 327 { | |
| 328 setState(PausedState); | |
| 329 m_pauseTime = time; | |
| 330 } | |
| 331 | |
| 332 void GraphicsLayerAnimation::resume() | |
| 333 { | |
| 334 setState(PlayingState); | |
| 335 m_totalRunningTime = m_pauseTime; | |
| 336 m_lastRefreshedTime = WTF::currentTime(); | |
| 337 } | |
| 338 | |
| 339 void GraphicsLayerAnimations::add(const GraphicsLayerAnimation& animation) | |
| 340 { | |
| 341 // Remove the old state if we are resuming a paused animation. | |
| 342 remove(animation.name(), animation.property()); | |
| 343 | |
| 344 m_animations.append(animation); | |
| 345 } | |
| 346 | |
| 347 void GraphicsLayerAnimations::pause(const String& name, double offset) | |
| 348 { | |
| 349 for (size_t i = 0; i < m_animations.size(); ++i) { | |
| 350 if (m_animations[i].name() == name) | |
| 351 m_animations[i].pause(offset); | |
| 352 } | |
| 353 } | |
| 354 | |
| 355 void GraphicsLayerAnimations::suspend(double offset) | |
| 356 { | |
| 357 for (size_t i = 0; i < m_animations.size(); ++i) | |
| 358 m_animations[i].pause(offset); | |
| 359 } | |
| 360 | |
| 361 void GraphicsLayerAnimations::resume() | |
| 362 { | |
| 363 for (size_t i = 0; i < m_animations.size(); ++i) | |
| 364 m_animations[i].resume(); | |
| 365 } | |
| 366 | |
| 367 void GraphicsLayerAnimations::remove(const String& name) | |
| 368 { | |
| 369 for (int i = m_animations.size() - 1; i >= 0; --i) { | |
| 370 if (m_animations[i].name() == name) | |
| 371 m_animations.remove(i); | |
| 372 } | |
| 373 } | |
| 374 | |
| 375 void GraphicsLayerAnimations::remove(const String& name, AnimatedPropertyID prop
erty) | |
| 376 { | |
| 377 for (int i = m_animations.size() - 1; i >= 0; --i) { | |
| 378 if (m_animations[i].name() == name && m_animations[i].property() == prop
erty) | |
| 379 m_animations.remove(i); | |
| 380 } | |
| 381 } | |
| 382 | |
| 383 void GraphicsLayerAnimations::apply(GraphicsLayerAnimation::Client* client) | |
| 384 { | |
| 385 for (size_t i = 0; i < m_animations.size(); ++i) | |
| 386 m_animations[i].apply(client); | |
| 387 } | |
| 388 | |
| 389 GraphicsLayerAnimations GraphicsLayerAnimations::getActiveAnimations() const | |
| 390 { | |
| 391 GraphicsLayerAnimations active; | |
| 392 for (size_t i = 0; i < m_animations.size(); ++i) { | |
| 393 if (m_animations[i].isActive()) | |
| 394 active.add(m_animations[i]); | |
| 395 } | |
| 396 return active; | |
| 397 } | |
| 398 } | |
| OLD | NEW |