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
13 namespace cc {
14
TransformOperations()15 TransformOperations::TransformOperations()
16 : decomposed_transform_dirty_(true) {
17 }
18
TransformOperations(const TransformOperations & other)19 TransformOperations::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
~TransformOperations()28 TransformOperations::~TransformOperations() {
29 }
30
Apply() const31 gfx::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
Blend(const TransformOperations & from,SkMScalar progress) const38 gfx::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
BlendedBoundsForBox(const gfx::BoxF & box,const TransformOperations & from,SkMScalar min_progress,SkMScalar max_progress,gfx::BoxF * bounds) const45 bool 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
MatchesTypes(const TransformOperations & other) const81 bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
82 if (IsIdentity() || other.IsIdentity())
83 return true;
84
85 if (operations_.size() != other.operations_.size())
86 return false;
87
88 for (size_t i = 0; i < operations_.size(); ++i) {
89 if (operations_[i].type != other.operations_[i].type
90 && !operations_[i].IsIdentity()
91 && !other.operations_[i].IsIdentity())
92 return false;
93 }
94
95 return true;
96 }
97
CanBlendWith(const TransformOperations & other) const98 bool TransformOperations::CanBlendWith(
99 const TransformOperations& other) const {
100 gfx::Transform dummy;
101 return BlendInternal(other, 0.5, &dummy);
102 }
103
AppendTranslate(SkMScalar x,SkMScalar y,SkMScalar z)104 void TransformOperations::AppendTranslate(SkMScalar x,
105 SkMScalar y,
106 SkMScalar z) {
107 TransformOperation to_add;
108 to_add.matrix.Translate3d(x, y, z);
109 to_add.type = TransformOperation::TransformOperationTranslate;
110 to_add.translate.x = x;
111 to_add.translate.y = y;
112 to_add.translate.z = z;
113 operations_.push_back(to_add);
114 decomposed_transform_dirty_ = true;
115 }
116
AppendRotate(SkMScalar x,SkMScalar y,SkMScalar z,SkMScalar degrees)117 void TransformOperations::AppendRotate(SkMScalar x,
118 SkMScalar y,
119 SkMScalar z,
120 SkMScalar degrees) {
121 TransformOperation to_add;
122 to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
123 to_add.type = TransformOperation::TransformOperationRotate;
124 to_add.rotate.axis.x = x;
125 to_add.rotate.axis.y = y;
126 to_add.rotate.axis.z = z;
127 to_add.rotate.angle = degrees;
128 operations_.push_back(to_add);
129 decomposed_transform_dirty_ = true;
130 }
131
AppendScale(SkMScalar x,SkMScalar y,SkMScalar z)132 void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) {
133 TransformOperation to_add;
134 to_add.matrix.Scale3d(x, y, z);
135 to_add.type = TransformOperation::TransformOperationScale;
136 to_add.scale.x = x;
137 to_add.scale.y = y;
138 to_add.scale.z = z;
139 operations_.push_back(to_add);
140 decomposed_transform_dirty_ = true;
141 }
142
AppendSkew(SkMScalar x,SkMScalar y)143 void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) {
144 TransformOperation to_add;
145 to_add.matrix.SkewX(x);
146 to_add.matrix.SkewY(y);
147 to_add.type = TransformOperation::TransformOperationSkew;
148 to_add.skew.x = x;
149 to_add.skew.y = y;
150 operations_.push_back(to_add);
151 decomposed_transform_dirty_ = true;
152 }
153
AppendPerspective(SkMScalar depth)154 void TransformOperations::AppendPerspective(SkMScalar depth) {
155 TransformOperation to_add;
156 to_add.matrix.ApplyPerspectiveDepth(depth);
157 to_add.type = TransformOperation::TransformOperationPerspective;
158 to_add.perspective_depth = depth;
159 operations_.push_back(to_add);
160 decomposed_transform_dirty_ = true;
161 }
162
AppendMatrix(const gfx::Transform & matrix)163 void TransformOperations::AppendMatrix(const gfx::Transform& matrix) {
164 TransformOperation to_add;
165 to_add.matrix = matrix;
166 to_add.type = TransformOperation::TransformOperationMatrix;
167 operations_.push_back(to_add);
168 decomposed_transform_dirty_ = true;
169 }
170
AppendIdentity()171 void TransformOperations::AppendIdentity() {
172 operations_.push_back(TransformOperation());
173 }
174
IsIdentity() const175 bool TransformOperations::IsIdentity() const {
176 for (size_t i = 0; i < operations_.size(); ++i) {
177 if (!operations_[i].IsIdentity())
178 return false;
179 }
180 return true;
181 }
182
BlendInternal(const TransformOperations & from,SkMScalar progress,gfx::Transform * result) const183 bool TransformOperations::BlendInternal(const TransformOperations& from,
184 SkMScalar progress,
185 gfx::Transform* result) const {
186 bool from_identity = from.IsIdentity();
187 bool to_identity = IsIdentity();
188 if (from_identity && to_identity)
189 return true;
190
191 if (MatchesTypes(from)) {
192 size_t num_operations =
193 std::max(from_identity ? 0 : from.operations_.size(),
194 to_identity ? 0 : operations_.size());
195 for (size_t i = 0; i < num_operations; ++i) {
196 gfx::Transform blended;
197 if (!TransformOperation::BlendTransformOperations(
198 from_identity ? 0 : &from.operations_[i],
199 to_identity ? 0 : &operations_[i],
200 progress,
201 &blended))
202 return false;
203 result->PreconcatTransform(blended);
204 }
205 return true;
206 }
207
208 if (!ComputeDecomposedTransform() || !from.ComputeDecomposedTransform())
209 return false;
210
211 gfx::DecomposedTransform to_return;
212 if (!gfx::BlendDecomposedTransforms(&to_return,
213 *decomposed_transform_.get(),
214 *from.decomposed_transform_.get(),
215 progress))
216 return false;
217
218 *result = ComposeTransform(to_return);
219 return true;
220 }
221
ComputeDecomposedTransform() const222 bool TransformOperations::ComputeDecomposedTransform() const {
223 if (decomposed_transform_dirty_) {
224 if (!decomposed_transform_)
225 decomposed_transform_.reset(new gfx::DecomposedTransform());
226 gfx::Transform transform = Apply();
227 if (!gfx::DecomposeTransform(decomposed_transform_.get(), transform))
228 return false;
229 decomposed_transform_dirty_ = false;
230 }
231 return true;
232 }
233
234 } // namespace cc
235