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/animation/tween.h"
10#include "ui/gfx/box_f.h"
11#include "ui/gfx/transform_util.h"
12#include "ui/gfx/vector3d_f.h"
13
14namespace cc {
15
16TransformOperations::TransformOperations()
17    : decomposed_transform_dirty_(true) {
18}
19
20TransformOperations::TransformOperations(const TransformOperations& other) {
21  operations_ = other.operations_;
22  decomposed_transform_dirty_ = other.decomposed_transform_dirty_;
23  if (!decomposed_transform_dirty_) {
24    decomposed_transform_.reset(
25        new gfx::DecomposedTransform(*other.decomposed_transform_.get()));
26  }
27}
28
29TransformOperations::~TransformOperations() {
30}
31
32gfx::Transform TransformOperations::Apply() const {
33  gfx::Transform to_return;
34  for (size_t i = 0; i < operations_.size(); ++i)
35    to_return.PreconcatTransform(operations_[i].matrix);
36  return to_return;
37}
38
39gfx::Transform TransformOperations::Blend(const TransformOperations& from,
40                                          SkMScalar progress) const {
41  gfx::Transform to_return;
42  BlendInternal(from, progress, &to_return);
43  return to_return;
44}
45
46bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF& box,
47                                              const TransformOperations& from,
48                                              SkMScalar min_progress,
49                                              SkMScalar max_progress,
50                                              gfx::BoxF* bounds) const {
51  *bounds = box;
52
53  bool from_identity = from.IsIdentity();
54  bool to_identity = IsIdentity();
55  if (from_identity && to_identity)
56    return true;
57
58  if (!MatchesTypes(from))
59    return false;
60
61  size_t num_operations =
62      std::max(from_identity ? 0 : from.operations_.size(),
63               to_identity ? 0 : operations_.size());
64
65  // Because we are squashing all of the matrices together when applying
66  // them to the animation, we must apply them in reverse order when
67  // not squashing them.
68  for (int i = num_operations - 1; i >= 0; --i) {
69    gfx::BoxF bounds_for_operation;
70    const TransformOperation* from_op =
71        from_identity ? NULL : &from.operations_[i];
72    const TransformOperation* to_op = to_identity ? NULL : &operations_[i];
73    if (!TransformOperation::BlendedBoundsForBox(*bounds,
74                                                 from_op,
75                                                 to_op,
76                                                 min_progress,
77                                                 max_progress,
78                                                 &bounds_for_operation))
79      return false;
80    *bounds = bounds_for_operation;
81  }
82
83  return true;
84}
85
86bool TransformOperations::AffectsScale() const {
87  for (size_t i = 0; i < operations_.size(); ++i) {
88    if (operations_[i].type == TransformOperation::TransformOperationScale)
89      return true;
90    if (operations_[i].type == TransformOperation::TransformOperationMatrix &&
91        !operations_[i].matrix.IsIdentityOrTranslation())
92      return true;
93  }
94  return false;
95}
96
97bool TransformOperations::IsTranslation() const {
98  for (size_t i = 0; i < operations_.size(); ++i) {
99    switch (operations_[i].type) {
100      case TransformOperation::TransformOperationIdentity:
101      case TransformOperation::TransformOperationTranslate:
102        continue;
103      case TransformOperation::TransformOperationMatrix:
104        if (!operations_[i].matrix.IsIdentityOrTranslation())
105          return false;
106        continue;
107      case TransformOperation::TransformOperationRotate:
108      case TransformOperation::TransformOperationScale:
109      case TransformOperation::TransformOperationSkew:
110      case TransformOperation::TransformOperationPerspective:
111        return false;
112    }
113  }
114  return true;
115}
116
117bool TransformOperations::ScaleComponent(gfx::Vector3dF* scale) const {
118  *scale = gfx::Vector3dF(1.f, 1.f, 1.f);
119  bool has_scale_component = false;
120  for (size_t i = 0; i < operations_.size(); ++i) {
121    switch (operations_[i].type) {
122      case TransformOperation::TransformOperationIdentity:
123      case TransformOperation::TransformOperationTranslate:
124        continue;
125      case TransformOperation::TransformOperationMatrix:
126        if (!operations_[i].matrix.IsIdentityOrTranslation())
127          return false;
128        continue;
129      case TransformOperation::TransformOperationRotate:
130      case TransformOperation::TransformOperationSkew:
131      case TransformOperation::TransformOperationPerspective:
132        return false;
133      case TransformOperation::TransformOperationScale:
134        if (has_scale_component)
135          return false;
136        has_scale_component = true;
137        scale->Scale(operations_[i].scale.x,
138                     operations_[i].scale.y,
139                     operations_[i].scale.z);
140    }
141  }
142  return true;
143}
144
145bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
146  if (IsIdentity() || other.IsIdentity())
147    return true;
148
149  if (operations_.size() != other.operations_.size())
150    return false;
151
152  for (size_t i = 0; i < operations_.size(); ++i) {
153    if (operations_[i].type != other.operations_[i].type
154      && !operations_[i].IsIdentity()
155      && !other.operations_[i].IsIdentity())
156      return false;
157  }
158
159  return true;
160}
161
162bool TransformOperations::CanBlendWith(
163    const TransformOperations& other) const {
164  gfx::Transform dummy;
165  return BlendInternal(other, 0.5, &dummy);
166}
167
168void TransformOperations::AppendTranslate(SkMScalar x,
169                                          SkMScalar y,
170                                          SkMScalar z) {
171  TransformOperation to_add;
172  to_add.matrix.Translate3d(x, y, z);
173  to_add.type = TransformOperation::TransformOperationTranslate;
174  to_add.translate.x = x;
175  to_add.translate.y = y;
176  to_add.translate.z = z;
177  operations_.push_back(to_add);
178  decomposed_transform_dirty_ = true;
179}
180
181void TransformOperations::AppendRotate(SkMScalar x,
182                                       SkMScalar y,
183                                       SkMScalar z,
184                                       SkMScalar degrees) {
185  TransformOperation to_add;
186  to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
187  to_add.type = TransformOperation::TransformOperationRotate;
188  to_add.rotate.axis.x = x;
189  to_add.rotate.axis.y = y;
190  to_add.rotate.axis.z = z;
191  to_add.rotate.angle = degrees;
192  operations_.push_back(to_add);
193  decomposed_transform_dirty_ = true;
194}
195
196void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) {
197  TransformOperation to_add;
198  to_add.matrix.Scale3d(x, y, z);
199  to_add.type = TransformOperation::TransformOperationScale;
200  to_add.scale.x = x;
201  to_add.scale.y = y;
202  to_add.scale.z = z;
203  operations_.push_back(to_add);
204  decomposed_transform_dirty_ = true;
205}
206
207void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) {
208  TransformOperation to_add;
209  to_add.matrix.SkewX(x);
210  to_add.matrix.SkewY(y);
211  to_add.type = TransformOperation::TransformOperationSkew;
212  to_add.skew.x = x;
213  to_add.skew.y = y;
214  operations_.push_back(to_add);
215  decomposed_transform_dirty_ = true;
216}
217
218void TransformOperations::AppendPerspective(SkMScalar depth) {
219  TransformOperation to_add;
220  to_add.matrix.ApplyPerspectiveDepth(depth);
221  to_add.type = TransformOperation::TransformOperationPerspective;
222  to_add.perspective_depth = depth;
223  operations_.push_back(to_add);
224  decomposed_transform_dirty_ = true;
225}
226
227void TransformOperations::AppendMatrix(const gfx::Transform& matrix) {
228  TransformOperation to_add;
229  to_add.matrix = matrix;
230  to_add.type = TransformOperation::TransformOperationMatrix;
231  operations_.push_back(to_add);
232  decomposed_transform_dirty_ = true;
233}
234
235void TransformOperations::AppendIdentity() {
236  operations_.push_back(TransformOperation());
237}
238
239bool TransformOperations::IsIdentity() const {
240  for (size_t i = 0; i < operations_.size(); ++i) {
241    if (!operations_[i].IsIdentity())
242      return false;
243  }
244  return true;
245}
246
247bool TransformOperations::BlendInternal(const TransformOperations& from,
248                                        SkMScalar progress,
249                                        gfx::Transform* result) const {
250  bool from_identity = from.IsIdentity();
251  bool to_identity = IsIdentity();
252  if (from_identity && to_identity)
253    return true;
254
255  if (MatchesTypes(from)) {
256    size_t num_operations =
257        std::max(from_identity ? 0 : from.operations_.size(),
258                 to_identity ? 0 : operations_.size());
259    for (size_t i = 0; i < num_operations; ++i) {
260      gfx::Transform blended;
261      if (!TransformOperation::BlendTransformOperations(
262          from_identity ? 0 : &from.operations_[i],
263          to_identity ? 0 : &operations_[i],
264          progress,
265          &blended))
266          return false;
267      result->PreconcatTransform(blended);
268    }
269    return true;
270  }
271
272  if (!ComputeDecomposedTransform() || !from.ComputeDecomposedTransform())
273    return false;
274
275  gfx::DecomposedTransform to_return;
276  if (!gfx::BlendDecomposedTransforms(&to_return,
277                                      *decomposed_transform_.get(),
278                                      *from.decomposed_transform_.get(),
279                                      progress))
280    return false;
281
282  *result = ComposeTransform(to_return);
283  return true;
284}
285
286bool TransformOperations::ComputeDecomposedTransform() const {
287  if (decomposed_transform_dirty_) {
288    if (!decomposed_transform_)
289      decomposed_transform_.reset(new gfx::DecomposedTransform());
290    gfx::Transform transform = Apply();
291    if (!gfx::DecomposeTransform(decomposed_transform_.get(), transform))
292      return false;
293    decomposed_transform_dirty_ = false;
294  }
295  return true;
296}
297
298}  // namespace cc
299