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