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