| Index: cc/transform_operations.cc
|
| diff --git a/cc/transform_operations.cc b/cc/transform_operations.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2e57d5152ceaeecc35a232dec13c658ebd0980b9
|
| --- /dev/null
|
| +++ b/cc/transform_operations.cc
|
| @@ -0,0 +1,383 @@
|
| +// Copyright 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include <algorithm>
|
| +#include <cmath>
|
| +#include <limits>
|
| +#include <vector>
|
| +
|
| +#include "cc/transform_operations.h"
|
| +
|
| +using WebKit::WebTransformationMatrix;
|
| +
|
| +namespace {
|
| +const double EPSILON = 1e-4;
|
| +}
|
| +
|
| +namespace cc {
|
| +
|
| +struct TransformOperation {
|
| + enum Type {
|
| + TransformOperationTranslate,
|
| + TransformOperationRotate,
|
| + TransformOperationScale,
|
| + TransformOperationSkew,
|
| + TransformOperationPerspective,
|
| + TransformOperationMatrix,
|
| + TransformOperationIdentity
|
| + };
|
| +
|
| + TransformOperation()
|
| + : type(TransformOperationIdentity) {
|
| + }
|
| +
|
| + Type type;
|
| + WebKit::WebTransformationMatrix matrix;
|
| +
|
| + union {
|
| + double perspective_depth;
|
| +
|
| + struct {
|
| + double x, y;
|
| + } skew;
|
| +
|
| + struct {
|
| + double x, y, z;
|
| + } scale;
|
| +
|
| + struct {
|
| + double x, y, z;
|
| + } translate;
|
| +
|
| + struct {
|
| + struct {
|
| + double x, y, z;
|
| + } axis;
|
| +
|
| + double angle;
|
| + } rotate;
|
| + };
|
| +
|
| + bool IsIdentity() const { return matrix.isIdentity(); }
|
| +};
|
| +
|
| +TransformOperations::TransformOperations() {
|
| + Initialize();
|
| +}
|
| +
|
| +TransformOperations::TransformOperations(const TransformOperations& other) {
|
| + Initialize(other);
|
| +}
|
| +
|
| +TransformOperations::~TransformOperations() {
|
| +}
|
| +
|
| +struct TransformOperationsPrivate {
|
| + std::vector<TransformOperation> operations;
|
| +};
|
| +
|
| +WebTransformationMatrix TransformOperations::Apply() const {
|
| + WebTransformationMatrix to_return;
|
| + for (size_t i = 0; i < private_->operations.size(); ++i)
|
| + to_return.multiply(private_->operations[i].matrix);
|
| + return to_return;
|
| +}
|
| +
|
| +static bool IsIdentity(const TransformOperation* operation) {
|
| + return !operation || operation->IsIdentity();
|
| +}
|
| +
|
| +static bool ShareSameAxis(const TransformOperation* from,
|
| + const TransformOperation* to,
|
| + double& axis_x, double& axis_y, double& axis_z,
|
| + double& angle_from) {
|
| + if (IsIdentity(from) && IsIdentity(to))
|
| + return false;
|
| +
|
| + if (IsIdentity(from) && !IsIdentity(to)) {
|
| + axis_x = to->rotate.axis.x;
|
| + axis_y = to->rotate.axis.y;
|
| + axis_z = to->rotate.axis.z;
|
| + angle_from = 0;
|
| + return true;
|
| + }
|
| +
|
| + if (!IsIdentity(from) && IsIdentity(to)) {
|
| + axis_x = from->rotate.axis.x;
|
| + axis_y = from->rotate.axis.y;
|
| + axis_z = from->rotate.axis.z;
|
| + angle_from = from->rotate.angle;
|
| + return true;
|
| + }
|
| +
|
| + double length_2 = from->rotate.axis.x * from->rotate.axis.x +
|
| + from->rotate.axis.y * from->rotate.axis.y +
|
| + from->rotate.axis.z * from->rotate.axis.z;
|
| + double other_length_2 = to->rotate.axis.x * to->rotate.axis.x +
|
| + to->rotate.axis.y * to->rotate.axis.y +
|
| + to->rotate.axis.z * to->rotate.axis.z;
|
| +
|
| + if (length_2 <= EPSILON || other_length_2 <= EPSILON)
|
| + return false;
|
| +
|
| + double dot = to->rotate.axis.x * from->rotate.axis.x +
|
| + to->rotate.axis.y * from->rotate.axis.y +
|
| + to->rotate.axis.z * from->rotate.axis.z;
|
| + double error = std::fabs(1.0 - (dot * dot) / (length_2 * other_length_2));
|
| + bool result = error < EPSILON;
|
| + if (result) {
|
| + axis_x = to->rotate.axis.x;
|
| + axis_y = to->rotate.axis.y;
|
| + axis_z = to->rotate.axis.z;
|
| + // If the axes are pointing in opposite directions, we need to reverse
|
| + // the angle.
|
| + angle_from = dot > 0 ? from->rotate.angle : -from->rotate.angle;
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +static double BlendDoubles(double from, double to, double progress) {
|
| + return from * (1 - progress) + to * progress;
|
| +}
|
| +
|
| +static bool BlendTransformOperations(const TransformOperation* from,
|
| + const TransformOperation* to,
|
| + double progress,
|
| + WebTransformationMatrix& result) {
|
| + if (IsIdentity(from) && IsIdentity(to))
|
| + return true;
|
| +
|
| + TransformOperation::Type interpolation_type =
|
| + TransformOperation::TransformOperationIdentity;
|
| + if (IsIdentity(to))
|
| + interpolation_type = from->type;
|
| + else
|
| + interpolation_type = to->type;
|
| +
|
| + switch (interpolation_type) {
|
| + case TransformOperation::TransformOperationTranslate: {
|
| + double from_x = IsIdentity(from) ? 0 : from->translate.x;
|
| + double from_y = IsIdentity(from) ? 0 : from->translate.y;
|
| + double from_z = IsIdentity(from) ? 0 : from->translate.z;
|
| + double to_x = IsIdentity(to) ? 0 : to->translate.x;
|
| + double to_y = IsIdentity(to) ? 0 : to->translate.y;
|
| + double to_z = IsIdentity(to) ? 0 : to->translate.z;
|
| + result.translate3d(BlendDoubles(from_x, to_x, progress),
|
| + BlendDoubles(from_y, to_y, progress),
|
| + BlendDoubles(from_z, to_z, progress));
|
| + break;
|
| + }
|
| + case TransformOperation::TransformOperationRotate: {
|
| + double axis_x = 0;
|
| + double axis_y = 0;
|
| + double axis_z = 1;
|
| + double from_angle = 0;
|
| + double to_angle = IsIdentity(to) ? 0 : to->rotate.angle;
|
| + if (ShareSameAxis(from, to, axis_x, axis_y, axis_z, from_angle))
|
| + result.rotate3d(axis_x, axis_y, axis_z,
|
| + BlendDoubles(from_angle, to_angle, progress));
|
| + else {
|
| + WebTransformationMatrix to_matrix;
|
| + if (!IsIdentity(to))
|
| + to_matrix = to->matrix;
|
| + WebTransformationMatrix from_matrix;
|
| + if (!IsIdentity(from))
|
| + from_matrix = from->matrix;
|
| + result = to_matrix;
|
| + if (!result.blend(from_matrix, progress))
|
| + return false;
|
| + }
|
| + break;
|
| + }
|
| + case TransformOperation::TransformOperationScale: {
|
| + double from_x = IsIdentity(from) ? 1 : from->scale.x;
|
| + double from_y = IsIdentity(from) ? 1 : from->scale.y;
|
| + double from_z = IsIdentity(from) ? 1 : from->scale.z;
|
| + double to_x = IsIdentity(to) ? 1 : to->scale.x;
|
| + double to_y = IsIdentity(to) ? 1 : to->scale.y;
|
| + double to_z = IsIdentity(to) ? 1 : to->scale.z;
|
| + result.scale3d(BlendDoubles(from_x, to_x, progress),
|
| + BlendDoubles(from_y, to_y, progress),
|
| + BlendDoubles(from_z, to_z, progress));
|
| + break;
|
| + }
|
| + case TransformOperation::TransformOperationSkew: {
|
| + double from_x = IsIdentity(from) ? 0 : from->skew.x;
|
| + double from_y = IsIdentity(from) ? 0 : from->skew.y;
|
| + double to_x = IsIdentity(to) ? 0 : to->skew.x;
|
| + double to_y = IsIdentity(to) ? 0 : to->skew.y;
|
| + result.skewX(BlendDoubles(from_x, to_x, progress));
|
| + result.skewY(BlendDoubles(from_y, to_y, progress));
|
| + break;
|
| + }
|
| + case TransformOperation::TransformOperationPerspective: {
|
| + double from_perspective_depth = IsIdentity(from) ?
|
| + std::numeric_limits<double>::max() : from->perspective_depth;
|
| + double to_perspective_depth = IsIdentity(to) ?
|
| + std::numeric_limits<double>::max() : to->perspective_depth;
|
| + result.applyPerspective(
|
| + BlendDoubles(from_perspective_depth, to_perspective_depth, progress));
|
| + break;
|
| + }
|
| + case TransformOperation::TransformOperationMatrix: {
|
| + WebTransformationMatrix to_matrix;
|
| + if (!IsIdentity(to))
|
| + to_matrix = to->matrix;
|
| + WebTransformationMatrix from_matrix;
|
| + if (!IsIdentity(from))
|
| + from_matrix = from->matrix;
|
| + result = to_matrix;
|
| + if (!result.blend(from_matrix, progress))
|
| + return false;
|
| + break;
|
| + }
|
| + case TransformOperation::TransformOperationIdentity:
|
| + // Do nothing.
|
| + break;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +WebTransformationMatrix TransformOperations::Blend(
|
| + const TransformOperations& from, double progress) const {
|
| + WebTransformationMatrix to_return;
|
| + BlendInternal(from, progress, to_return);
|
| + return to_return;
|
| +}
|
| +
|
| +bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
|
| + if (IsIdentity() || other.IsIdentity())
|
| + return true;
|
| +
|
| + if (private_->operations.size() != other.private_->operations.size())
|
| + return false;
|
| +
|
| + for (size_t i = 0; i < private_->operations.size(); ++i) {
|
| + if (private_->operations[i].type != other.private_->operations[i].type
|
| + && !private_->operations[i].IsIdentity()
|
| + && !other.private_->operations[i].IsIdentity())
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool TransformOperations::CanBlendWith(
|
| + const TransformOperations& other) const {
|
| + WebTransformationMatrix dummy;
|
| + return BlendInternal(other, 0.5, dummy);
|
| +}
|
| +
|
| +void TransformOperations::AppendTranslate(double x, double y, double z) {
|
| + TransformOperation to_add;
|
| + to_add.matrix.translate3d(x, y, z);
|
| + to_add.type = TransformOperation::TransformOperationTranslate;
|
| + to_add.translate.x = x;
|
| + to_add.translate.y = y;
|
| + to_add.translate.z = z;
|
| + private_->operations.push_back(to_add);
|
| +}
|
| +
|
| +void TransformOperations::AppendRotate(double x, double y, double z,
|
| + double degrees) {
|
| + TransformOperation to_add;
|
| + to_add.matrix.rotate3d(x, y, z, degrees);
|
| + to_add.type = TransformOperation::TransformOperationRotate;
|
| + to_add.rotate.axis.x = x;
|
| + to_add.rotate.axis.y = y;
|
| + to_add.rotate.axis.z = z;
|
| + to_add.rotate.angle = degrees;
|
| + private_->operations.push_back(to_add);
|
| +}
|
| +
|
| +void TransformOperations::AppendScale(double x, double y, double z) {
|
| + TransformOperation to_add;
|
| + to_add.matrix.scale3d(x, y, z);
|
| + to_add.type = TransformOperation::TransformOperationScale;
|
| + to_add.scale.x = x;
|
| + to_add.scale.y = y;
|
| + to_add.scale.z = z;
|
| + private_->operations.push_back(to_add);
|
| +}
|
| +
|
| +void TransformOperations::AppendSkew(double x, double y) {
|
| + TransformOperation to_add;
|
| + to_add.matrix.skewX(x);
|
| + to_add.matrix.skewY(y);
|
| + to_add.type = TransformOperation::TransformOperationSkew;
|
| + to_add.skew.x = x;
|
| + to_add.skew.y = y;
|
| + private_->operations.push_back(to_add);
|
| +}
|
| +
|
| +void TransformOperations::AppendPerspective(double depth) {
|
| + TransformOperation to_add;
|
| + to_add.matrix.applyPerspective(depth);
|
| + to_add.type = TransformOperation::TransformOperationPerspective;
|
| + to_add.perspective_depth = depth;
|
| + private_->operations.push_back(to_add);
|
| +}
|
| +
|
| +void TransformOperations::AppendMatrix(const WebTransformationMatrix& matrix) {
|
| + TransformOperation to_add;
|
| + to_add.matrix = matrix;
|
| + to_add.type = TransformOperation::TransformOperationMatrix;
|
| + private_->operations.push_back(to_add);
|
| +}
|
| +
|
| +void TransformOperations::AppendIdentity() {
|
| + private_->operations.push_back(TransformOperation());
|
| +}
|
| +
|
| +bool TransformOperations::IsIdentity() const {
|
| + for (size_t i = 0; i < private_->operations.size(); ++i) {
|
| + if (!private_->operations[i].IsIdentity())
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void TransformOperations::Initialize() {
|
| + private_.reset(new TransformOperationsPrivate);
|
| +}
|
| +
|
| +void TransformOperations::Initialize(const TransformOperations& other) {
|
| + if (private_.get() != other.private_.get())
|
| + private_.reset(new TransformOperationsPrivate(*other.private_.get()));
|
| + else
|
| + Initialize();
|
| +}
|
| +
|
| +bool TransformOperations::BlendInternal(const TransformOperations& from,
|
| + double progress,
|
| + WebTransformationMatrix& result) const {
|
| + bool from_identity = from.IsIdentity();
|
| + bool to_identity = IsIdentity();
|
| + if (from_identity && to_identity)
|
| + return true;
|
| +
|
| + if (MatchesTypes(from)) {
|
| + size_t num_operations =
|
| + std::max(from_identity ? 0 : from.private_->operations.size(),
|
| + to_identity ? 0 : private_->operations.size());
|
| + for (size_t i = 0; i < num_operations; ++i) {
|
| + WebTransformationMatrix blended;
|
| + if (!BlendTransformOperations(
|
| + from_identity ? 0 : &from.private_->operations[i],
|
| + to_identity ? 0 : &private_->operations[i],
|
| + progress,
|
| + blended))
|
| + return false;
|
| + result.multiply(blended);
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + result = Apply();
|
| + WebTransformationMatrix from_transform = from.Apply();
|
| + return result.blend(from_transform, progress);
|
| +}
|
| +
|
| +} // namespace cc
|
|
|