TEST(TransformationMatrixTest, NonInvertableBlendTest) { TransformationMatrix from; TransformationMatrix to(2.7133590938, 0.0, 0.0, 0.0, 0.0, 2.4645137761, 0.0, 0.0, 0.0, 0.0, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05); TransformationMatrix result; result = to; result.blend(from, 0.25); EXPECT_EQ(result, from); result = to; result.blend(from, 0.75); EXPECT_EQ(result, to); }
Ref<TransformOperation> PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) return *this; if (blendToIdentity) { double p = floatValueForLength(m_p, 1); p = WebCore::blend(p, 1.0, progress); // FIXME: this seems wrong. https://bugs.webkit.org/show_bug.cgi?id=52700 return PerspectiveTransformOperation::create(Length(clampToPositiveInteger(p), Fixed)); } const PerspectiveTransformOperation* fromOp = downcast<PerspectiveTransformOperation>(from); Length fromP = fromOp ? fromOp->m_p : Length(m_p.type()); Length toP = m_p; TransformationMatrix fromT; TransformationMatrix toT; fromT.applyPerspective(floatValueForLength(fromP, 1)); toT.applyPerspective(floatValueForLength(toP, 1)); toT.blend(fromT, progress); TransformationMatrix::Decomposed4Type decomp; if (toT.decompose4(decomp)) { if (decomp.perspectiveZ) { double val = -1.0 / decomp.perspectiveZ; return PerspectiveTransformOperation::create(Length(clampToPositiveInteger(val), Fixed)); } } return PerspectiveTransformOperation::create(Length(0, Fixed)); }
PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) return this; if (blendToIdentity) { double p = WebCore::blend(m_p, 1., progress); // FIXME: this seems wrong. https://bugs.webkit.org/show_bug.cgi?id=52700 return PerspectiveTransformOperation::create(clampToPositiveInteger(p)); } const PerspectiveTransformOperation* fromOp = static_cast<const PerspectiveTransformOperation*>(from); TransformationMatrix fromT; TransformationMatrix toT; fromT.applyPerspective(fromOp ? fromOp->m_p : 0); toT.applyPerspective(m_p); toT.blend(fromT, progress); TransformationMatrix::DecomposedType decomp; toT.decompose(decomp); if (decomp.perspectiveZ) { double val = -1.0 / decomp.perspectiveZ; return PerspectiveTransformOperation::create(clampToPositiveInteger(val)); } return PerspectiveTransformOperation::create(0); }
void AndroidTransformAnimation::applyForProgress(LayerAndroid* layer, float progress) { // First, we need to get the from and to values int from, to; pickValues(progress, &from, &to); TransformAnimationValue* fromValue = (TransformAnimationValue*) m_operations->at(from); TransformAnimationValue* toValue = (TransformAnimationValue*) m_operations->at(to); ALOGV("[layer %d] fromValue %x, key %.2f, toValue %x, key %.2f for progress %.2f", layer->uniqueId(), fromValue, fromValue->keyTime(), toValue, toValue->keyTime(), progress); // We now have the correct two values to work with, let's compute the // progress value const TimingFunction* timingFunction = fromValue->timingFunction(); float p = applyTimingFunction(fromValue->keyTime(), toValue->keyTime(), progress, timingFunction); ALOGV("progress %.2f => %.2f from: %.2f to: %.2f", progress, p, fromValue->keyTime(), toValue->keyTime()); progress = p; // With both values and the progress, we also need to check out that // the operations are compatible (i.e. we are animating the same number // of values; if not we do a matrix blend) TransformationMatrix transformMatrix; bool valid = true; unsigned int fromSize = fromValue->value()->size(); if (fromSize) { if (toValue->value()->size() != fromSize) valid = false; else { for (unsigned int j = 0; j < fromSize && valid; j++) { if (!fromValue->value()->operations()[j]->isSameType( *toValue->value()->operations()[j])) valid = false; } } } IntSize size(layer->getSize().width(), layer->getSize().height()); if (valid) { for (size_t i = 0; i < toValue->value()->size(); ++i) toValue->value()->operations()[i]->blend(fromValue->value()->at(i), progress)->apply(transformMatrix, size); } else { TransformationMatrix source; fromValue->value()->apply(size, source); toValue->value()->apply(size, transformMatrix); transformMatrix.blend(source, progress); } // Set the final transform on the layer layer->setTransform(transformMatrix); }
void InterpolatedTransformOperation::apply(TransformationMatrix& transform, const FloatSize& borderBoxSize) const { TransformationMatrix fromTransform; TransformationMatrix toTransform; from.apply(borderBoxSize, fromTransform); to.apply(borderBoxSize, toTransform); toTransform.blend(fromTransform, progress); transform.multiply(toTransform); }
static TransformationMatrix applyTransformAnimation(const TransformOperations& from, const TransformOperations& to, double progress, const FloatSize& boxSize, bool listsMatch) { TransformationMatrix matrix; // First frame of an animation. if (!progress) { from.apply(boxSize, matrix); return matrix; } // Last frame of an animation. if (progress == 1) { to.apply(boxSize, matrix); return matrix; } // If we have incompatible operation lists, we blend the resulting matrices. if (!listsMatch) { TransformationMatrix fromMatrix; to.apply(boxSize, matrix); from.apply(boxSize, fromMatrix); matrix.blend(fromMatrix, progress); return matrix; } // Animation to "-webkit-transform: none". if (!to.size()) { TransformOperations blended(from); for (auto& operation : blended.operations()) operation->blend(nullptr, progress, true)->apply(matrix, boxSize); return matrix; } // Animation from "-webkit-transform: none". if (!from.size()) { TransformOperations blended(to); for (auto& operation : blended.operations()) operation->blend(nullptr, 1 - progress, true)->apply(matrix, boxSize); return matrix; } // Normal animation with a matching operation list. TransformOperations blended(to); for (size_t i = 0; i < blended.operations().size(); ++i) blended.operations()[i]->blend(from.at(i), progress, !from.at(i))->apply(matrix, boxSize); return matrix; }
TransformOperations TransformOperations::blendByUsingMatrixInterpolation(const TransformOperations& from, double progress, const LayoutSize& size) const { TransformOperations result; // Convert the TransformOperations into matrices TransformationMatrix fromTransform; TransformationMatrix toTransform; from.apply(size, fromTransform); apply(size, toTransform); toTransform.blend(fromTransform, progress); // Append the result result.operations().append(Matrix3DTransformOperation::create(toTransform)); return result; }
PassRefPtr<TransformOperation> Matrix3DTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) return this; // Convert the TransformOperations into matrices FloatSize size; TransformationMatrix fromT; TransformationMatrix toT; if (from) from->apply(fromT, size); apply(toT, size); if (blendToIdentity) std::swap(fromT, toT); toT.blend(fromT, progress); return Matrix3DTransformOperation::create(toT); }
PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) return this; if (blendToIdentity) return PerspectiveTransformOperation::create(m_p + (1. - m_p) * progress); const PerspectiveTransformOperation* fromOp = static_cast<const PerspectiveTransformOperation*>(from); double fromP = fromOp ? fromOp->m_p : 0; double toP = m_p; TransformationMatrix fromT; TransformationMatrix toT; fromT.applyPerspective(fromP); toT.applyPerspective(toP); toT.blend(fromT, progress); TransformationMatrix::DecomposedType decomp; toT.decompose(decomp); return PerspectiveTransformOperation::create(decomp.perspectiveZ ? -1.0 / decomp.perspectiveZ : 0.0); }
TransformationMatrix CCKeyframedTransformAnimationCurve::getValue(double t, const IntSize& layerSize) const { TransformationMatrix transformMatrix; if (t <= m_keyframes.first()->time()) { m_keyframes.first()->value().apply(layerSize, transformMatrix); return transformMatrix; } if (t >= m_keyframes.last()->time()) { m_keyframes.last()->value().apply(layerSize, transformMatrix); return transformMatrix; } size_t i = 0; for (; i < m_keyframes.size() - 1; ++i) { if (t < m_keyframes[i+1]->time()) break; } double progress = (t - m_keyframes[i]->time()) / (m_keyframes[i+1]->time() - m_keyframes[i]->time()); if (m_keyframes[i]->timingFunction()) progress = m_keyframes[i]->timingFunction()->getValue(progress); if (m_keyframes[i]->value().operationsMatch(m_keyframes[i+1]->value())) { for (size_t j = 0; j < m_keyframes[i+1]->value().size(); ++j) m_keyframes[i+1]->value().operations()[j]->blend(m_keyframes[i]->value().at(j), progress)->apply(transformMatrix, layerSize); } else { TransformationMatrix source; m_keyframes[i]->value().apply(layerSize, source); m_keyframes[i+1]->value().apply(layerSize, transformMatrix); transformMatrix.blend(source, progress); } return transformMatrix; }
PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) return this; if (blendToIdentity) return RotateTransformOperation::create(m_x, m_y, m_z, m_angle - m_angle * progress, m_type); const RotateTransformOperation* fromOp = static_cast<const RotateTransformOperation*>(from); // Optimize for single axis rotation if (!fromOp || (fromOp->m_x == 0 && fromOp->m_y == 0 && fromOp->m_z == 1) || (fromOp->m_x == 0 && fromOp->m_y == 1 && fromOp->m_z == 0) || (fromOp->m_x == 1 && fromOp->m_y == 0 && fromOp->m_z == 0)) { double fromAngle = fromOp ? fromOp->m_angle : 0; return RotateTransformOperation::create(fromOp ? fromOp->m_x : m_x, fromOp ? fromOp->m_y : m_y, fromOp ? fromOp->m_z : m_z, UI::blend(fromAngle, m_angle, progress), m_type); } const RotateTransformOperation* toOp = this; // Create the 2 rotation matrices TransformationMatrix fromT; TransformationMatrix toT; fromT.rotate3d((float)(fromOp ? fromOp->m_x : 0), (float)(fromOp ? fromOp->m_y : 0), (float)(fromOp ? fromOp->m_z : 1), (float)(fromOp ? fromOp->m_angle : 0)); toT.rotate3d((float)(toOp ? toOp->m_x : 0), (float)(toOp ? toOp->m_y : 0), (float)(toOp ? toOp->m_z : 1), (float)(toOp ? toOp->m_angle : 0)); // Blend them toT.blend(fromT, progress); // Extract the result as a quaternion TransformationMatrix::DecomposedType decomp; toT.decompose(decomp); // Convert that to Axis/Angle form double x = -decomp.quaternionX; double y = -decomp.quaternionY; double z = -decomp.quaternionZ; double length = sqrt(x * x + y * y + z * z); double angle = 0; if (length > 0.00001) { x /= length; y /= length; z /= length; angle = rad2deg(acos(decomp.quaternionW) * 2); } else { x = 0; y = 0; z = 1; } return RotateTransformOperation::create(x, y, z, angle, ROTATE_3D); }
static Ref<TransformOperation> createOperation(TransformationMatrix& to, TransformationMatrix& from, double progress) { to.blend(from, progress); return Matrix3DTransformOperation::create(to); }