transform_operations.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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  for (size_t i = 0; i < num_operations; ++i) {
65    gfx::BoxF bounds_for_operation;
66    const TransformOperation* from_op =
67        from_identity ? NULL : &from.operations_[i];
68    const TransformOperation* to_op = to_identity ? NULL : &operations_[i];
69    if (!TransformOperation::BlendedBoundsForBox(*bounds,
70                                                 from_op,
71                                                 to_op,
72                                                 min_progress,
73                                                 max_progress,
74                                                 &bounds_for_operation))
75      return false;
76    *bounds = bounds_for_operation;
77  }
78
79  return true;
80}
81
82bool TransformOperations::AffectsScale() const {
83  for (size_t i = 0; i < operations_.size(); ++i) {
84    if (operations_[i].type == TransformOperation::TransformOperationScale)
85      return true;
86    if (operations_[i].type == TransformOperation::TransformOperationMatrix &&
87        !operations_[i].matrix.IsIdentityOrTranslation())
88      return true;
89  }
90  return false;
91}
92
93bool TransformOperations::IsTranslation() const {
94  for (size_t i = 0; i < operations_.size(); ++i) {
95    switch (operations_[i].type) {
96      case TransformOperation::TransformOperationIdentity:
97      case TransformOperation::TransformOperationTranslate:
98        continue;
99      case TransformOperation::TransformOperationMatrix:
100        if (!operations_[i].matrix.IsIdentityOrTranslation())
101          return false;
102        continue;
103      case TransformOperation::TransformOperationRotate:
104      case TransformOperation::TransformOperationScale:
105      case TransformOperation::TransformOperationSkew:
106      case TransformOperation::TransformOperationPerspective:
107        return false;
108    }
109  }
110  return true;
111}
112
113bool TransformOperations::MaximumScale(const TransformOperations& from,
114                                       SkMScalar min_progress,
115                                       SkMScalar max_progress,
116                                       float* max_scale) const {
117  if (!MatchesTypes(from))
118    return false;
119
120  gfx::Vector3dF from_scale;
121  gfx::Vector3dF to_scale;
122
123  if (!from.ScaleComponent(&from_scale) || !ScaleComponent(&to_scale))
124    return false;
125
126  gfx::Vector3dF scale_at_min_progress(
127      std::abs(gfx::Tween::FloatValueBetween(
128          min_progress, from_scale.x(), to_scale.x())),
129      std::abs(gfx::Tween::FloatValueBetween(
130          min_progress, from_scale.y(), to_scale.y())),
131      std::abs(gfx::Tween::FloatValueBetween(
132          min_progress, from_scale.z(), to_scale.z())));
133  gfx::Vector3dF scale_at_max_progress(
134      std::abs(gfx::Tween::FloatValueBetween(
135          max_progress, from_scale.x(), to_scale.x())),
136      std::abs(gfx::Tween::FloatValueBetween(
137          max_progress, from_scale.y(), to_scale.y())),
138      std::abs(gfx::Tween::FloatValueBetween(
139          max_progress, from_scale.z(), to_scale.z())));
140
141  gfx::Vector3dF max_scale_3d = scale_at_min_progress;
142  max_scale_3d.SetToMax(scale_at_max_progress);
143  *max_scale =
144      std::max(max_scale_3d.x(), std::max(max_scale_3d.y(), max_scale_3d.z()));
145  return true;
146}
147
148bool TransformOperations::ScaleComponent(gfx::Vector3dF* scale) const {
149  *scale = gfx::Vector3dF(1.f, 1.f, 1.f);
150  bool has_scale_component = false;
151  for (size_t i = 0; i < operations_.size(); ++i) {
152    switch (operations_[i].type) {
153      case TransformOperation::TransformOperationIdentity:
154      case TransformOperation::TransformOperationTranslate:
155        continue;
156      case TransformOperation::TransformOperationMatrix:
157        if (!operations_[i].matrix.IsIdentityOrTranslation())
158          return false;
159        continue;
160      case TransformOperation::TransformOperationRotate:
161      case TransformOperation::TransformOperationSkew:
162      case TransformOperation::TransformOperationPerspective:
163        return false;
164      case TransformOperation::TransformOperationScale:
165        if (has_scale_component)
166          return false;
167        has_scale_component = true;
168        scale->Scale(operations_[i].scale.x,
169                     operations_[i].scale.y,
170                     operations_[i].scale.z);
171    }
172  }
173  return true;
174}
175
176bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
177  if (IsIdentity() || other.IsIdentity())
178    return true;
179
180  if (operations_.size() != other.operations_.size())
181    return false;
182
183  for (size_t i = 0; i < operations_.size(); ++i) {
184    if (operations_[i].type != other.operations_[i].type
185      && !operations_[i].IsIdentity()
186      && !other.operations_[i].IsIdentity())
187      return false;
188  }
189
190  return true;
191}
192
193bool TransformOperations::CanBlendWith(
194    const TransformOperations& other) const {
195  gfx::Transform dummy;
196  return BlendInternal(other, 0.5, &dummy);
197}
198
199void TransformOperations::AppendTranslate(SkMScalar x,
200                                          SkMScalar y,
201                                          SkMScalar z) {
202  TransformOperation to_add;
203  to_add.matrix.Translate3d(x, y, z);
204  to_add.type = TransformOperation::TransformOperationTranslate;
205  to_add.translate.x = x;
206  to_add.translate.y = y;
207  to_add.translate.z = z;
208  operations_.push_back(to_add);
209  decomposed_transform_dirty_ = true;
210}
211
212void TransformOperations::AppendRotate(SkMScalar x,
213                                       SkMScalar y,
214                                       SkMScalar z,
215                                       SkMScalar degrees) {
216  TransformOperation to_add;
217  to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
218  to_add.type = TransformOperation::TransformOperationRotate;
219  to_add.rotate.axis.x = x;
220  to_add.rotate.axis.y = y;
221  to_add.rotate.axis.z = z;
222  to_add.rotate.angle = degrees;
223  operations_.push_back(to_add);
224  decomposed_transform_dirty_ = true;
225}
226
227void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) {
228  TransformOperation to_add;
229  to_add.matrix.Scale3d(x, y, z);
230  to_add.type = TransformOperation::TransformOperationScale;
231  to_add.scale.x = x;
232  to_add.scale.y = y;
233  to_add.scale.z = z;
234  operations_.push_back(to_add);
235  decomposed_transform_dirty_ = true;
236}
237
238void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) {
239  TransformOperation to_add;
240  to_add.matrix.SkewX(x);
241  to_add.matrix.SkewY(y);
242  to_add.type = TransformOperation::TransformOperationSkew;
243  to_add.skew.x = x;
244  to_add.skew.y = y;
245  operations_.push_back(to_add);
246  decomposed_transform_dirty_ = true;
247}
248
249void TransformOperations::AppendPerspective(SkMScalar depth) {
250  TransformOperation to_add;
251  to_add.matrix.ApplyPerspectiveDepth(depth);
252  to_add.type = TransformOperation::TransformOperationPerspective;
253  to_add.perspective_depth = depth;
254  operations_.push_back(to_add);
255  decomposed_transform_dirty_ = true;
256}
257
258void TransformOperations::AppendMatrix(const gfx::Transform& matrix) {
259  TransformOperation to_add;
260  to_add.matrix = matrix;
261  to_add.type = TransformOperation::TransformOperationMatrix;
262  operations_.push_back(to_add);
263  decomposed_transform_dirty_ = true;
264}
265
266void TransformOperations::AppendIdentity() {
267  operations_.push_back(TransformOperation());
268}
269
270bool TransformOperations::IsIdentity() const {
271  for (size_t i = 0; i < operations_.size(); ++i) {
272    if (!operations_[i].IsIdentity())
273      return false;
274  }
275  return true;
276}
277
278bool TransformOperations::BlendInternal(const TransformOperations& from,
279                                        SkMScalar progress,
280                                        gfx::Transform* result) const {
281  bool from_identity = from.IsIdentity();
282  bool to_identity = IsIdentity();
283  if (from_identity && to_identity)
284    return true;
285
286  if (MatchesTypes(from)) {
287    size_t num_operations =
288        std::max(from_identity ? 0 : from.operations_.size(),
289                 to_identity ? 0 : operations_.size());
290    for (size_t i = 0; i < num_operations; ++i) {
291      gfx::Transform blended;
292      if (!TransformOperation::BlendTransformOperations(
293          from_identity ? 0 : &from.operations_[i],
294          to_identity ? 0 : &operations_[i],
295          progress,
296          &blended))
297          return false;
298      result->PreconcatTransform(blended);
299    }
300    return true;
301  }
302
303  if (!ComputeDecomposedTransform() || !from.ComputeDecomposedTransform())
304    return false;
305
306  gfx::DecomposedTransform to_return;
307  if (!gfx::BlendDecomposedTransforms(&to_return,
308                                      *decomposed_transform_.get(),
309                                      *from.decomposed_transform_.get(),
310                                      progress))
311    return false;
312
313  *result = ComposeTransform(to_return);
314  return true;
315}
316
317bool TransformOperations::ComputeDecomposedTransform() const {
318  if (decomposed_transform_dirty_) {
319    if (!decomposed_transform_)
320      decomposed_transform_.reset(new gfx::DecomposedTransform());
321    gfx::Transform transform = Apply();
322    if (!gfx::DecomposeTransform(decomposed_transform_.get(), transform))
323      return false;
324    decomposed_transform_dirty_ = false;
325  }
326  return true;
327}
328
329}  // namespace cc
330