Home | History | Annotate | Download | only in animation
      1 /*
      2  * Copyright (C) 2012 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1.  Redistributions of source code must retain the above copyright
      8  *     notice, this list of conditions and the following disclaimer.
      9  * 2.  Redistributions in binary form must reproduce the above copyright
     10  *     notice, this list of conditions and the following disclaimer in the
     11  *     documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
     20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23  */
     24 
     25 #include "config.h"
     26 
     27 #include "core/platform/animation/AnimationTranslationUtil.h"
     28 
     29 #include "core/platform/animation/CSSAnimationData.h"
     30 #include "core/platform/animation/KeyframeValueList.h"
     31 #include "platform/LengthFunctions.h"
     32 #include "platform/geometry/FloatSize.h"
     33 #include "platform/graphics/filters/SkiaImageFilterBuilder.h"
     34 #include "platform/transforms/InterpolatedTransformOperation.h"
     35 #include "platform/transforms/Matrix3DTransformOperation.h"
     36 #include "platform/transforms/MatrixTransformOperation.h"
     37 #include "platform/transforms/PerspectiveTransformOperation.h"
     38 #include "platform/transforms/RotateTransformOperation.h"
     39 #include "platform/transforms/ScaleTransformOperation.h"
     40 #include "platform/transforms/SkewTransformOperation.h"
     41 #include "platform/transforms/TransformationMatrix.h"
     42 #include "platform/transforms/TranslateTransformOperation.h"
     43 
     44 #include "public/platform/Platform.h"
     45 #include "public/platform/WebAnimation.h"
     46 #include "public/platform/WebAnimationCurve.h"
     47 #include "public/platform/WebCompositorSupport.h"
     48 #include "public/platform/WebFilterAnimationCurve.h"
     49 #include "public/platform/WebFloatAnimationCurve.h"
     50 #include "public/platform/WebTransformAnimationCurve.h"
     51 
     52 #include "wtf/OwnPtr.h"
     53 #include "wtf/text/CString.h"
     54 
     55 using namespace std;
     56 using namespace blink;
     57 
     58 namespace WebCore {
     59 
     60 void toWebTransformOperations(const TransformOperations& transformOperations, const FloatSize& boxSize, WebTransformOperations* webTransformOperations)
     61 {
     62     // We need to do a deep copy the transformOperations may contain ref pointers to TransformOperation objects.
     63     for (size_t j = 0; j < transformOperations.size(); ++j) {
     64         switch (transformOperations.operations()[j]->type()) {
     65         case TransformOperation::ScaleX:
     66         case TransformOperation::ScaleY:
     67         case TransformOperation::ScaleZ:
     68         case TransformOperation::Scale3D:
     69         case TransformOperation::Scale: {
     70             ScaleTransformOperation* transform = static_cast<ScaleTransformOperation*>(transformOperations.operations()[j].get());
     71             webTransformOperations->appendScale(transform->x(), transform->y(), transform->z());
     72             break;
     73         }
     74         case TransformOperation::TranslateX:
     75         case TransformOperation::TranslateY:
     76         case TransformOperation::TranslateZ:
     77         case TransformOperation::Translate3D:
     78         case TransformOperation::Translate: {
     79             TranslateTransformOperation* transform = static_cast<TranslateTransformOperation*>(transformOperations.operations()[j].get());
     80             webTransformOperations->appendTranslate(floatValueForLength(transform->x(), boxSize.width()), floatValueForLength(transform->y(), boxSize.height()), floatValueForLength(transform->z(), 1));
     81             break;
     82         }
     83         case TransformOperation::RotateX:
     84         case TransformOperation::RotateY:
     85         case TransformOperation::Rotate3D:
     86         case TransformOperation::Rotate: {
     87             RotateTransformOperation* transform = static_cast<RotateTransformOperation*>(transformOperations.operations()[j].get());
     88             webTransformOperations->appendRotate(transform->x(), transform->y(), transform->z(), transform->angle());
     89             break;
     90         }
     91         case TransformOperation::SkewX:
     92         case TransformOperation::SkewY:
     93         case TransformOperation::Skew: {
     94             SkewTransformOperation* transform = static_cast<SkewTransformOperation*>(transformOperations.operations()[j].get());
     95             webTransformOperations->appendSkew(transform->angleX(), transform->angleY());
     96             break;
     97         }
     98         case TransformOperation::Matrix: {
     99             MatrixTransformOperation* transform = static_cast<MatrixTransformOperation*>(transformOperations.operations()[j].get());
    100             TransformationMatrix m = transform->matrix();
    101             webTransformOperations->appendMatrix(TransformationMatrix::toSkMatrix44(m));
    102             break;
    103         }
    104         case TransformOperation::Matrix3D: {
    105             Matrix3DTransformOperation* transform = static_cast<Matrix3DTransformOperation*>(transformOperations.operations()[j].get());
    106             TransformationMatrix m = transform->matrix();
    107             webTransformOperations->appendMatrix(TransformationMatrix::toSkMatrix44(m));
    108             break;
    109         }
    110         case TransformOperation::Perspective: {
    111             PerspectiveTransformOperation* transform = static_cast<PerspectiveTransformOperation*>(transformOperations.operations()[j].get());
    112             webTransformOperations->appendPerspective(floatValueForLength(transform->perspective(), 0));
    113             break;
    114         }
    115         case TransformOperation::Interpolated: {
    116             TransformationMatrix m;
    117             transformOperations.operations()[j]->apply(m, boxSize);
    118             webTransformOperations->appendMatrix(TransformationMatrix::toSkMatrix44(m));
    119             break;
    120         }
    121         case TransformOperation::Identity:
    122             webTransformOperations->appendIdentity();
    123             break;
    124         case TransformOperation::None:
    125             // Do nothing.
    126             break;
    127         } // switch
    128     } // for each operation
    129 }
    130 
    131 template <class Value, class Keyframe, class Curve>
    132 bool appendKeyframeWithStandardTimingFunction(Curve* curve, double keyTime, const Value* value, const Value* lastValue, blink::WebAnimationCurve::TimingFunctionType timingFunctionType, const FloatSize&)
    133 {
    134     curve->add(Keyframe(keyTime, value->value()), timingFunctionType);
    135     return true;
    136 }
    137 
    138 template <class Value, class Keyframe, class Curve>
    139 bool appendKeyframeWithCustomBezierTimingFunction(Curve* curve, double keyTime, const Value* value, const Value* lastValue, double x1, double y1, double x2, double y2, const FloatSize&)
    140 {
    141     curve->add(Keyframe(keyTime, value->value()), x1, y1, x2, y2);
    142     return true;
    143 }
    144 
    145 template <>
    146 bool appendKeyframeWithStandardTimingFunction<TransformAnimationValue, WebTransformKeyframe, WebTransformAnimationCurve>(WebTransformAnimationCurve* curve, double keyTime, const TransformAnimationValue* value, const TransformAnimationValue* lastValue, blink::WebAnimationCurve::TimingFunctionType timingFunctionType, const FloatSize& boxSize)
    147 {
    148     bool canBlend = !lastValue;
    149     OwnPtr<WebTransformOperations> operations = adoptPtr(Platform::current()->compositorSupport()->createTransformOperations());
    150     if (!operations)
    151         return false;
    152     toWebTransformOperations(*value->value(), boxSize, operations.get());
    153     if (!canBlend) {
    154         OwnPtr<WebTransformOperations> lastOperations = adoptPtr(Platform::current()->compositorSupport()->createTransformOperations());
    155         if (!lastOperations)
    156             return false;
    157         toWebTransformOperations(*lastValue->value(), boxSize, lastOperations.get());
    158         canBlend = lastOperations->canBlendWith(*operations);
    159     }
    160     if (canBlend) {
    161         curve->add(WebTransformKeyframe(keyTime, operations.release()), timingFunctionType);
    162         return true;
    163     }
    164     return false;
    165 }
    166 
    167 template <>
    168 bool appendKeyframeWithCustomBezierTimingFunction<TransformAnimationValue, WebTransformKeyframe, WebTransformAnimationCurve>(WebTransformAnimationCurve* curve, double keyTime, const TransformAnimationValue* value, const TransformAnimationValue* lastValue, double x1, double y1, double x2, double y2, const FloatSize& boxSize)
    169 {
    170     bool canBlend = !lastValue;
    171     OwnPtr<WebTransformOperations> operations = adoptPtr(Platform::current()->compositorSupport()->createTransformOperations());
    172     if (!operations)
    173         return false;
    174     toWebTransformOperations(*value->value(), boxSize, operations.get());
    175     if (!canBlend) {
    176         OwnPtr<WebTransformOperations> lastOperations = adoptPtr(Platform::current()->compositorSupport()->createTransformOperations());
    177         if (!lastOperations)
    178             return false;
    179         toWebTransformOperations(*lastValue->value(), boxSize, lastOperations.get());
    180         canBlend = lastOperations->canBlendWith(*operations);
    181     }
    182     if (canBlend) {
    183         curve->add(WebTransformKeyframe(keyTime, operations.release()), x1, y1, x2, y2);
    184         return true;
    185     }
    186     return false;
    187 }
    188 
    189 bool toWebFilterOperations(const FilterOperations& inOperations, WebFilterOperations* outOperations)
    190 {
    191     SkiaImageFilterBuilder builder;
    192     FilterOutsets outsets = inOperations.outsets();
    193     builder.setCropOffset(FloatSize(outsets.left(), outsets.top()));
    194     return builder.buildFilterOperations(inOperations, outOperations);
    195 }
    196 
    197 template <>
    198 bool appendKeyframeWithStandardTimingFunction<FilterAnimationValue, WebFilterKeyframe, WebFilterAnimationCurve>(WebFilterAnimationCurve* curve, double keyTime, const FilterAnimationValue* value, const FilterAnimationValue* lastValue, blink::WebAnimationCurve::TimingFunctionType timingFunctionType, const FloatSize& boxSize)
    199 {
    200     // FIXME(ajuma): In order to animate pixel-moving filters on the compositor thread, we need
    201     // to update overlap testing to take into account the bounds within which the animation
    202     // will be contained, and we need the compositor to update layer bounds as the animation
    203     // progresses.
    204     if (value->value()->hasFilterThatMovesPixels())
    205         return false;
    206     OwnPtr<WebFilterOperations> operations = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations());
    207     if (!toWebFilterOperations(*(value->value()), operations.get()))
    208         return false;
    209     curve->add(WebFilterKeyframe(keyTime, operations.release()), timingFunctionType);
    210     return true;
    211 }
    212 
    213 template <>
    214 bool appendKeyframeWithCustomBezierTimingFunction<FilterAnimationValue, WebFilterKeyframe, WebFilterAnimationCurve>(WebFilterAnimationCurve* curve, double keyTime, const FilterAnimationValue* value, const FilterAnimationValue* lastValue, double x1, double y1, double x2, double y2, const FloatSize& boxSize)
    215 {
    216     // FIXME(ajuma): In order to animate pixel-moving filters on the compositor thread, we need
    217     // to update overlap testing to take into account the bounds within which the animation
    218     // will be contained, and we need the compositor to update layer bounds as the animation
    219     // progresses.
    220     if (value->value()->hasFilterThatMovesPixels())
    221         return false;
    222 
    223     OwnPtr<WebFilterOperations> operations = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations());
    224     if (!toWebFilterOperations(*(value->value()), operations.get()))
    225         return false;
    226     curve->add(WebFilterKeyframe(keyTime, operations.release()), x1, y1, x2, y2);
    227     return true;
    228 }
    229 
    230 template <class Value, class Keyframe, class Curve>
    231 PassOwnPtr<blink::WebAnimation> createWebAnimation(const KeyframeValueList& valueList, const CSSAnimationData* animation, int animationId, double timeOffset, Curve* curve, blink::WebAnimation::TargetProperty targetProperty, const FloatSize& boxSize)
    232 {
    233     bool alternate = false;
    234     bool reverse = false;
    235     if (animation && animation->isDirectionSet()) {
    236         CSSAnimationData::AnimationDirection direction = animation->direction();
    237         if (direction == CSSAnimationData::AnimationDirectionAlternate || direction == CSSAnimationData::AnimationDirectionAlternateReverse)
    238             alternate = true;
    239         if (direction == CSSAnimationData::AnimationDirectionReverse || direction == CSSAnimationData::AnimationDirectionAlternateReverse)
    240             reverse = true;
    241     }
    242 
    243     for (size_t i = 0; i < valueList.size(); i++) {
    244         size_t index = reverse ? valueList.size() - i - 1 : i;
    245         const Value* originalValue = static_cast<const Value*>(valueList.at(index));
    246         const Value* lastOriginalValue = 0;
    247         if (valueList.size() > 1 && ((reverse && index + 1 < valueList.size()) || (!reverse && index > 0)))
    248             lastOriginalValue = static_cast<const Value*>(valueList.at(reverse ? index + 1 : index - 1));
    249 
    250         const TimingFunction* originalTimingFunction = originalValue->timingFunction();
    251 
    252         // If there hasn't been a timing function associated with this keyframe, use the
    253         // animation's timing function, if we have one.
    254         if (!originalTimingFunction && animation->isTimingFunctionSet())
    255             originalTimingFunction = animation->timingFunction();
    256 
    257         // Ease is the default timing function.
    258         blink::WebAnimationCurve::TimingFunctionType timingFunctionType = blink::WebAnimationCurve::TimingFunctionTypeEase;
    259 
    260         bool isUsingCustomBezierTimingFunction = false;
    261         double x1 = 0;
    262         double y1 = 0;
    263         double x2 = 1;
    264         double y2 = 1;
    265 
    266         if (originalTimingFunction) {
    267             switch (originalTimingFunction->type()) {
    268             case TimingFunction::StepsFunction:
    269                 // FIXME: add support for steps timing function.
    270                 return nullptr;
    271             case TimingFunction::LinearFunction:
    272                 // This doesn't need to be flipped when the animation is reversed.
    273                 timingFunctionType = blink::WebAnimationCurve::TimingFunctionTypeLinear;
    274                 break;
    275             case TimingFunction::CubicBezierFunction:
    276                 {
    277                     const CubicBezierTimingFunction* originalBezierTimingFunction = toCubicBezierTimingFunction(originalTimingFunction);
    278                     isUsingCustomBezierTimingFunction = true;
    279                     x1 = originalBezierTimingFunction->x1();
    280                     y1 = originalBezierTimingFunction->y1();
    281                     x2 = originalBezierTimingFunction->x2();
    282                     y2 = originalBezierTimingFunction->y2();
    283                     if (reverse) {
    284                         // When the animation is reversed, we need to swap the
    285                         // start and end keyframes, and flip the timing
    286                         // function in both x and y.
    287                         double x1Old = x1;
    288                         double y1Old = y1;
    289                         x1 = 1 - x2;
    290                         y1 = 1 - y2;
    291                         x2 = 1 - x1Old;
    292                         y2 = 1 - y1Old;
    293                     }
    294                     break;
    295                 }
    296             default:
    297                 ASSERT_NOT_REACHED();
    298             } // switch
    299         }
    300 
    301         double duration = (animation && animation->isDurationSet()) ? animation->duration() : 1;
    302         double keyTime = originalValue->keyTime() * duration;
    303 
    304         if (reverse)
    305             keyTime = duration - keyTime;
    306 
    307         bool addedKeyframe = false;
    308         if (isUsingCustomBezierTimingFunction)
    309             addedKeyframe = appendKeyframeWithCustomBezierTimingFunction<Value, Keyframe, Curve>(curve, keyTime, originalValue, lastOriginalValue, x1, y1, x2, y2, boxSize);
    310         else
    311             addedKeyframe = appendKeyframeWithStandardTimingFunction<Value, Keyframe, Curve>(curve, keyTime, originalValue, lastOriginalValue, timingFunctionType, boxSize);
    312 
    313         if (!addedKeyframe)
    314             return nullptr;
    315     }
    316 
    317     OwnPtr<blink::WebAnimation> webAnimation = adoptPtr(Platform::current()->compositorSupport()->createAnimation(*curve, targetProperty, animationId));
    318 
    319     int iterations = (animation && animation->isIterationCountSet()) ? animation->iterationCount() : 1;
    320     webAnimation->setIterations(iterations);
    321     webAnimation->setAlternatesDirection(alternate);
    322 
    323     // If timeOffset > 0, then the animation has started in the past.
    324     webAnimation->setTimeOffset(timeOffset);
    325 
    326     return webAnimation.release();
    327 }
    328 
    329 PassOwnPtr<blink::WebAnimation> createWebAnimation(const KeyframeValueList& values, const CSSAnimationData* animation, int animationId, double timeOffset, const FloatSize& boxSize)
    330 {
    331     switch (values.property()) {
    332     case AnimatedPropertyWebkitTransform: {
    333         OwnPtr<WebTransformAnimationCurve> curve = adoptPtr(Platform::current()->compositorSupport()->createTransformAnimationCurve());
    334         return createWebAnimation<TransformAnimationValue, WebTransformKeyframe, WebTransformAnimationCurve>(values, animation, animationId, timeOffset, curve.get(), blink::WebAnimation::TargetPropertyTransform, FloatSize(boxSize));
    335     }
    336 
    337     case AnimatedPropertyOpacity: {
    338         OwnPtr<WebFloatAnimationCurve> curve = adoptPtr(Platform::current()->compositorSupport()->createFloatAnimationCurve());
    339         return createWebAnimation<FloatAnimationValue, WebFloatKeyframe, WebFloatAnimationCurve>(values, animation, animationId, timeOffset, curve.get(), blink::WebAnimation::TargetPropertyOpacity, FloatSize());
    340     }
    341 
    342     case AnimatedPropertyWebkitFilter: {
    343         OwnPtr<WebFilterAnimationCurve> curve = adoptPtr(Platform::current()->compositorSupport()->createFilterAnimationCurve());
    344         return createWebAnimation<FilterAnimationValue, WebFilterKeyframe, WebFilterAnimationCurve>(values, animation, animationId, timeOffset, curve.get(), blink::WebAnimation::TargetPropertyFilter, FloatSize(boxSize));
    345     }
    346 
    347     case AnimatedPropertyBackgroundColor:
    348     case AnimatedPropertyInvalid:
    349         return nullptr;
    350     }
    351 
    352     return nullptr;
    353 }
    354 
    355 } // namespace WebCore
    356