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.
5 #include "cc/animation/transform_operations.h"
9 #include "ui/gfx/animation/tween.h"
10 #include "ui/gfx/geometry/box_f.h"
11 #include "ui/gfx/geometry/vector3d_f.h"
12 #include "ui/gfx/transform_util.h"
16 TransformOperations::TransformOperations()
17 : decomposed_transform_dirty_(true) {
20 TransformOperations::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()));
29 TransformOperations::~TransformOperations() {
32 gfx::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
);
39 gfx::Transform
TransformOperations::Blend(const TransformOperations
& from
,
40 SkMScalar progress
) const {
41 gfx::Transform to_return
;
42 BlendInternal(from
, progress
, &to_return
);
46 bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF
& box
,
47 const TransformOperations
& from
,
48 SkMScalar min_progress
,
49 SkMScalar max_progress
,
50 gfx::BoxF
* bounds
) const {
53 bool from_identity
= from
.IsIdentity();
54 bool to_identity
= IsIdentity();
55 if (from_identity
&& to_identity
)
58 if (!MatchesTypes(from
))
61 size_t num_operations
= std::max(from_identity
? 0 : from
.operations_
.size(),
62 to_identity
? 0 : operations_
.size());
64 // Because we are squashing all of the matrices together when applying
65 // them to the animation, we must apply them in reverse order when
66 // not squashing them.
67 for (size_t i
= 0; i
< num_operations
; ++i
) {
68 size_t operation_index
= num_operations
- 1 - i
;
69 gfx::BoxF bounds_for_operation
;
70 const TransformOperation
* from_op
=
71 from_identity
? nullptr : &from
.operations_
[operation_index
];
72 const TransformOperation
* to_op
=
73 to_identity
? nullptr : &operations_
[operation_index
];
74 if (!TransformOperation::BlendedBoundsForBox(*bounds
, from_op
, to_op
,
75 min_progress
, max_progress
,
76 &bounds_for_operation
)) {
79 *bounds
= bounds_for_operation
;
85 bool TransformOperations::AffectsScale() const {
86 for (size_t i
= 0; i
< operations_
.size(); ++i
) {
87 if (operations_
[i
].type
== TransformOperation::TRANSFORM_OPERATION_SCALE
)
89 if (operations_
[i
].type
== TransformOperation::TRANSFORM_OPERATION_MATRIX
&&
90 !operations_
[i
].matrix
.IsIdentityOrTranslation())
96 bool TransformOperations::PreservesAxisAlignment() const {
97 for (size_t i
= 0; i
< operations_
.size(); ++i
) {
98 switch (operations_
[i
].type
) {
99 case TransformOperation::TRANSFORM_OPERATION_IDENTITY
:
100 case TransformOperation::TRANSFORM_OPERATION_TRANSLATE
:
101 case TransformOperation::TRANSFORM_OPERATION_SCALE
:
103 case TransformOperation::TRANSFORM_OPERATION_MATRIX
:
104 if (!operations_
[i
].matrix
.IsIdentity() &&
105 !operations_
[i
].matrix
.IsScaleOrTranslation())
108 case TransformOperation::TRANSFORM_OPERATION_ROTATE
:
109 case TransformOperation::TRANSFORM_OPERATION_SKEW
:
110 case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE
:
117 bool TransformOperations::IsTranslation() const {
118 for (size_t i
= 0; i
< operations_
.size(); ++i
) {
119 switch (operations_
[i
].type
) {
120 case TransformOperation::TRANSFORM_OPERATION_IDENTITY
:
121 case TransformOperation::TRANSFORM_OPERATION_TRANSLATE
:
123 case TransformOperation::TRANSFORM_OPERATION_MATRIX
:
124 if (!operations_
[i
].matrix
.IsIdentityOrTranslation())
127 case TransformOperation::TRANSFORM_OPERATION_ROTATE
:
128 case TransformOperation::TRANSFORM_OPERATION_SCALE
:
129 case TransformOperation::TRANSFORM_OPERATION_SKEW
:
130 case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE
:
137 bool TransformOperations::ScaleComponent(gfx::Vector3dF
* scale
) const {
138 *scale
= gfx::Vector3dF(1.f
, 1.f
, 1.f
);
139 bool has_scale_component
= false;
140 for (size_t i
= 0; i
< operations_
.size(); ++i
) {
141 switch (operations_
[i
].type
) {
142 case TransformOperation::TRANSFORM_OPERATION_IDENTITY
:
143 case TransformOperation::TRANSFORM_OPERATION_TRANSLATE
:
145 case TransformOperation::TRANSFORM_OPERATION_MATRIX
:
146 if (!operations_
[i
].matrix
.IsIdentityOrTranslation())
149 case TransformOperation::TRANSFORM_OPERATION_ROTATE
:
150 case TransformOperation::TRANSFORM_OPERATION_SKEW
:
151 case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE
:
153 case TransformOperation::TRANSFORM_OPERATION_SCALE
:
154 if (has_scale_component
)
156 has_scale_component
= true;
157 scale
->Scale(operations_
[i
].scale
.x
,
158 operations_
[i
].scale
.y
,
159 operations_
[i
].scale
.z
);
165 bool TransformOperations::MatchesTypes(const TransformOperations
& other
) const {
166 if (operations_
.size() == 0 || other
.operations_
.size() == 0)
169 if (operations_
.size() != other
.operations_
.size())
172 for (size_t i
= 0; i
< operations_
.size(); ++i
) {
173 if (operations_
[i
].type
!= other
.operations_
[i
].type
)
180 bool TransformOperations::CanBlendWith(
181 const TransformOperations
& other
) const {
182 gfx::Transform dummy
;
183 return BlendInternal(other
, 0.5, &dummy
);
186 void TransformOperations::AppendTranslate(SkMScalar x
,
189 TransformOperation to_add
;
190 to_add
.matrix
.Translate3d(x
, y
, z
);
191 to_add
.type
= TransformOperation::TRANSFORM_OPERATION_TRANSLATE
;
192 to_add
.translate
.x
= x
;
193 to_add
.translate
.y
= y
;
194 to_add
.translate
.z
= z
;
195 operations_
.push_back(to_add
);
196 decomposed_transform_dirty_
= true;
199 void TransformOperations::AppendRotate(SkMScalar x
,
203 TransformOperation to_add
;
204 to_add
.matrix
.RotateAbout(gfx::Vector3dF(x
, y
, z
), degrees
);
205 to_add
.type
= TransformOperation::TRANSFORM_OPERATION_ROTATE
;
206 to_add
.rotate
.axis
.x
= x
;
207 to_add
.rotate
.axis
.y
= y
;
208 to_add
.rotate
.axis
.z
= z
;
209 to_add
.rotate
.angle
= degrees
;
210 operations_
.push_back(to_add
);
211 decomposed_transform_dirty_
= true;
214 void TransformOperations::AppendScale(SkMScalar x
, SkMScalar y
, SkMScalar z
) {
215 TransformOperation to_add
;
216 to_add
.matrix
.Scale3d(x
, y
, z
);
217 to_add
.type
= TransformOperation::TRANSFORM_OPERATION_SCALE
;
221 operations_
.push_back(to_add
);
222 decomposed_transform_dirty_
= true;
225 void TransformOperations::AppendSkew(SkMScalar x
, SkMScalar y
) {
226 TransformOperation to_add
;
227 to_add
.matrix
.SkewX(x
);
228 to_add
.matrix
.SkewY(y
);
229 to_add
.type
= TransformOperation::TRANSFORM_OPERATION_SKEW
;
232 operations_
.push_back(to_add
);
233 decomposed_transform_dirty_
= true;
236 void TransformOperations::AppendPerspective(SkMScalar depth
) {
237 TransformOperation to_add
;
238 to_add
.matrix
.ApplyPerspectiveDepth(depth
);
239 to_add
.type
= TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE
;
240 to_add
.perspective_depth
= depth
;
241 operations_
.push_back(to_add
);
242 decomposed_transform_dirty_
= true;
245 void TransformOperations::AppendMatrix(const gfx::Transform
& matrix
) {
246 TransformOperation to_add
;
247 to_add
.matrix
= matrix
;
248 to_add
.type
= TransformOperation::TRANSFORM_OPERATION_MATRIX
;
249 operations_
.push_back(to_add
);
250 decomposed_transform_dirty_
= true;
253 void TransformOperations::AppendIdentity() {
254 operations_
.push_back(TransformOperation());
257 bool TransformOperations::IsIdentity() const {
258 for (size_t i
= 0; i
< operations_
.size(); ++i
) {
259 if (!operations_
[i
].IsIdentity())
265 bool TransformOperations::BlendInternal(const TransformOperations
& from
,
267 gfx::Transform
* result
) const {
268 bool from_identity
= from
.IsIdentity();
269 bool to_identity
= IsIdentity();
270 if (from_identity
&& to_identity
)
273 if (MatchesTypes(from
)) {
274 size_t num_operations
=
275 std::max(from_identity
? 0 : from
.operations_
.size(),
276 to_identity
? 0 : operations_
.size());
277 for (size_t i
= 0; i
< num_operations
; ++i
) {
278 gfx::Transform blended
;
279 if (!TransformOperation::BlendTransformOperations(
280 from
.operations_
.size() <= i
? 0 : &from
.operations_
[i
],
281 operations_
.size() <= i
? 0 : &operations_
[i
], progress
,
284 result
->PreconcatTransform(blended
);
289 if (!ComputeDecomposedTransform() || !from
.ComputeDecomposedTransform())
292 gfx::DecomposedTransform to_return
;
293 if (!gfx::BlendDecomposedTransforms(&to_return
,
294 *decomposed_transform_
.get(),
295 *from
.decomposed_transform_
.get(),
299 *result
= ComposeTransform(to_return
);
303 bool TransformOperations::ComputeDecomposedTransform() const {
304 if (decomposed_transform_dirty_
) {
305 if (!decomposed_transform_
)
306 decomposed_transform_
.reset(new gfx::DecomposedTransform());
307 gfx::Transform transform
= Apply();
308 if (!gfx::DecomposeTransform(decomposed_transform_
.get(), transform
))
310 decomposed_transform_dirty_
= false;