1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cc/animation/transform_operations.h"
6
7#include <algorithm>
8
9#include "ui/gfx/transform_util.h"
10#include "ui/gfx/vector3d_f.h"
11
12namespace cc {
13
14TransformOperations::TransformOperations()
15    : decomposed_transform_dirty_(true) {
16}
17
18TransformOperations::TransformOperations(const TransformOperations& other) {
19  operations_ = other.operations_;
20  decomposed_transform_dirty_ = other.decomposed_transform_dirty_;
21  if (!decomposed_transform_dirty_) {
22    decomposed_transform_.reset(
23        new gfx::DecomposedTransform(*other.decomposed_transform_.get()));
24  }
25}
26
27TransformOperations::~TransformOperations() {
28}
29
30gfx::Transform TransformOperations::Apply() const {
31  gfx::Transform to_return;
32  for (size_t i = 0; i < operations_.size(); ++i)
33    to_return.PreconcatTransform(operations_[i].matrix);
34  return to_return;
35}
36
37gfx::Transform TransformOperations::Blend(
38    const TransformOperations& from, double progress) const {
39  gfx::Transform to_return;
40  BlendInternal(from, progress, &to_return);
41  return to_return;
42}
43
44bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
45  if (IsIdentity() || other.IsIdentity())
46    return true;
47
48  if (operations_.size() != other.operations_.size())
49    return false;
50
51  for (size_t i = 0; i < operations_.size(); ++i) {
52    if (operations_[i].type != other.operations_[i].type
53      && !operations_[i].IsIdentity()
54      && !other.operations_[i].IsIdentity())
55      return false;
56  }
57
58  return true;
59}
60
61bool TransformOperations::CanBlendWith(
62    const TransformOperations& other) const {
63  gfx::Transform dummy;
64  return BlendInternal(other, 0.5, &dummy);
65}
66
67void TransformOperations::AppendTranslate(double x, double y, double z) {
68  TransformOperation to_add;
69  to_add.matrix.Translate3d(x, y, z);
70  to_add.type = TransformOperation::TransformOperationTranslate;
71  to_add.translate.x = x;
72  to_add.translate.y = y;
73  to_add.translate.z = z;
74  operations_.push_back(to_add);
75  decomposed_transform_dirty_ = true;
76}
77
78void TransformOperations::AppendRotate(double x, double y, double z,
79                                       double degrees) {
80  TransformOperation to_add;
81  to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
82  to_add.type = TransformOperation::TransformOperationRotate;
83  to_add.rotate.axis.x = x;
84  to_add.rotate.axis.y = y;
85  to_add.rotate.axis.z = z;
86  to_add.rotate.angle = degrees;
87  operations_.push_back(to_add);
88  decomposed_transform_dirty_ = true;
89}
90
91void TransformOperations::AppendScale(double x, double y, double z) {
92  TransformOperation to_add;
93  to_add.matrix.Scale3d(x, y, z);
94  to_add.type = TransformOperation::TransformOperationScale;
95  to_add.scale.x = x;
96  to_add.scale.y = y;
97  to_add.scale.z = z;
98  operations_.push_back(to_add);
99  decomposed_transform_dirty_ = true;
100}
101
102void TransformOperations::AppendSkew(double x, double y) {
103  TransformOperation to_add;
104  to_add.matrix.SkewX(x);
105  to_add.matrix.SkewY(y);
106  to_add.type = TransformOperation::TransformOperationSkew;
107  to_add.skew.x = x;
108  to_add.skew.y = y;
109  operations_.push_back(to_add);
110  decomposed_transform_dirty_ = true;
111}
112
113void TransformOperations::AppendPerspective(double depth) {
114  TransformOperation to_add;
115  to_add.matrix.ApplyPerspectiveDepth(depth);
116  to_add.type = TransformOperation::TransformOperationPerspective;
117  to_add.perspective_depth = depth;
118  operations_.push_back(to_add);
119  decomposed_transform_dirty_ = true;
120}
121
122void TransformOperations::AppendMatrix(const gfx::Transform& matrix) {
123  TransformOperation to_add;
124  to_add.matrix = matrix;
125  to_add.type = TransformOperation::TransformOperationMatrix;
126  operations_.push_back(to_add);
127  decomposed_transform_dirty_ = true;
128}
129
130void TransformOperations::AppendIdentity() {
131  operations_.push_back(TransformOperation());
132}
133
134bool TransformOperations::IsIdentity() const {
135  for (size_t i = 0; i < operations_.size(); ++i) {
136    if (!operations_[i].IsIdentity())
137      return false;
138  }
139  return true;
140}
141
142bool TransformOperations::BlendInternal(const TransformOperations& from,
143                                        double progress,
144                                        gfx::Transform* result) const {
145  bool from_identity = from.IsIdentity();
146  bool to_identity = IsIdentity();
147  if (from_identity && to_identity)
148    return true;
149
150  if (MatchesTypes(from)) {
151    size_t num_operations =
152        std::max(from_identity ? 0 : from.operations_.size(),
153                 to_identity ? 0 : operations_.size());
154    for (size_t i = 0; i < num_operations; ++i) {
155      gfx::Transform blended;
156      if (!TransformOperation::BlendTransformOperations(
157          from_identity ? 0 : &from.operations_[i],
158          to_identity ? 0 : &operations_[i],
159          progress,
160          &blended))
161          return false;
162      result->PreconcatTransform(blended);
163    }
164    return true;
165  }
166
167  if (!ComputeDecomposedTransform() || !from.ComputeDecomposedTransform())
168    return false;
169
170  gfx::DecomposedTransform to_return;
171  if (!gfx::BlendDecomposedTransforms(&to_return,
172                                      *decomposed_transform_.get(),
173                                      *from.decomposed_transform_.get(),
174                                      progress))
175    return false;
176
177  *result = ComposeTransform(to_return);
178  return true;
179}
180
181bool TransformOperations::ComputeDecomposedTransform() const {
182  if (decomposed_transform_dirty_) {
183    if (!decomposed_transform_)
184      decomposed_transform_.reset(new gfx::DecomposedTransform());
185    gfx::Transform transform = Apply();
186    if (!gfx::DecomposeTransform(decomposed_transform_.get(), transform))
187      return false;
188    decomposed_transform_dirty_ = false;
189  }
190  return true;
191}
192
193}  // namespace cc
194