1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SVGTransformListSMILType.h"
9 #include "mozilla/SMILValue.h"
11 #include "SVGTransformList.h"
12 #include "SVGTransform.h"
15 using namespace mozilla::dom::SVGTransform_Binding
;
19 using TransformArray
= FallibleTArray
<SVGTransformSMILData
>;
21 //----------------------------------------------------------------------
22 // nsISMILType implementation
24 void SVGTransformListSMILType::Init(SMILValue
& aValue
) const {
25 MOZ_ASSERT(aValue
.IsNull(), "Unexpected value type");
27 aValue
.mU
.mPtr
= new TransformArray(1);
31 void SVGTransformListSMILType::Destroy(SMILValue
& aValue
) const {
32 MOZ_ASSERT(aValue
.mType
== this, "Unexpected SMIL value type");
33 TransformArray
* params
= static_cast<TransformArray
*>(aValue
.mU
.mPtr
);
35 aValue
.mU
.mPtr
= nullptr;
36 aValue
.mType
= SMILNullType::Singleton();
39 nsresult
SVGTransformListSMILType::Assign(SMILValue
& aDest
,
40 const SMILValue
& aSrc
) const {
41 MOZ_ASSERT(aDest
.mType
== aSrc
.mType
, "Incompatible SMIL types");
42 MOZ_ASSERT(aDest
.mType
== this, "Unexpected SMIL value");
44 const TransformArray
* srcTransforms
=
45 static_cast<const TransformArray
*>(aSrc
.mU
.mPtr
);
46 TransformArray
* dstTransforms
= static_cast<TransformArray
*>(aDest
.mU
.mPtr
);
47 if (!dstTransforms
->Assign(*srcTransforms
, fallible
)) {
48 return NS_ERROR_OUT_OF_MEMORY
;
54 bool SVGTransformListSMILType::IsEqual(const SMILValue
& aLeft
,
55 const SMILValue
& aRight
) const {
56 MOZ_ASSERT(aLeft
.mType
== aRight
.mType
, "Incompatible SMIL types");
57 MOZ_ASSERT(aLeft
.mType
== this, "Unexpected SMIL type");
59 const TransformArray
& leftArr(
60 *static_cast<const TransformArray
*>(aLeft
.mU
.mPtr
));
61 const TransformArray
& rightArr(
62 *static_cast<const TransformArray
*>(aRight
.mU
.mPtr
));
64 // If array-lengths don't match, we're trivially non-equal.
65 if (leftArr
.Length() != rightArr
.Length()) {
69 // Array-lengths match -- check each array-entry for equality.
70 uint32_t length
= leftArr
.Length(); // == rightArr->Length(), if we get here
71 for (uint32_t i
= 0; i
< length
; ++i
) {
72 if (leftArr
[i
] != rightArr
[i
]) {
77 // Found no differences.
81 nsresult
SVGTransformListSMILType::Add(SMILValue
& aDest
,
82 const SMILValue
& aValueToAdd
,
83 uint32_t aCount
) const {
84 MOZ_ASSERT(aDest
.mType
== this, "Unexpected SMIL type");
85 MOZ_ASSERT(aDest
.mType
== aValueToAdd
.mType
, "Incompatible SMIL types");
87 TransformArray
& dstTransforms(*static_cast<TransformArray
*>(aDest
.mU
.mPtr
));
88 const TransformArray
& srcTransforms(
89 *static_cast<const TransformArray
*>(aValueToAdd
.mU
.mPtr
));
91 // We're doing a simple add here (as opposed to a sandwich add below).
92 // We only do this when we're accumulating a repeat result or calculating
93 // a by-animation value.
95 // In either case we should have 1 transform in the source array.
96 NS_ASSERTION(srcTransforms
.Length() == 1,
97 "Invalid source transform list to add");
99 // And we should have 0 or 1 transforms in the dest array.
100 // (We can have 0 transforms in the case of by-animation when we are
101 // calculating the by-value as "0 + by". Zero being represented by a
102 // SMILValue with an empty transform array.)
103 NS_ASSERTION(dstTransforms
.Length() < 2,
104 "Invalid dest transform list to add to");
106 // Get the individual transforms to add
107 const SVGTransformSMILData
& srcTransform
= srcTransforms
[0];
108 if (dstTransforms
.IsEmpty()) {
109 SVGTransformSMILData
* result
= dstTransforms
.AppendElement(
110 SVGTransformSMILData(srcTransform
.mTransformType
), fallible
);
111 NS_ENSURE_TRUE(result
, NS_ERROR_OUT_OF_MEMORY
);
113 SVGTransformSMILData
& dstTransform
= dstTransforms
[0];
115 // The types must be the same
116 NS_ASSERTION(srcTransform
.mTransformType
== dstTransform
.mTransformType
,
117 "Trying to perform simple add of different transform types");
119 // And it should be impossible that one of them is of matrix type
120 NS_ASSERTION(srcTransform
.mTransformType
!= SVG_TRANSFORM_MATRIX
,
121 "Trying to perform simple add with matrix transform");
123 // Add the parameters
124 for (int i
= 0; i
<= 2; ++i
) {
125 dstTransform
.mParams
[i
] += srcTransform
.mParams
[i
] * aCount
;
131 nsresult
SVGTransformListSMILType::SandwichAdd(
132 SMILValue
& aDest
, const SMILValue
& aValueToAdd
) const {
133 MOZ_ASSERT(aDest
.mType
== this, "Unexpected SMIL type");
134 MOZ_ASSERT(aDest
.mType
== aValueToAdd
.mType
, "Incompatible SMIL types");
136 // For <animateTransform> a sandwich add means a matrix post-multiplication
137 // which just means to put the additional transform on the end of the array
139 TransformArray
& dstTransforms(*static_cast<TransformArray
*>(aDest
.mU
.mPtr
));
140 const TransformArray
& srcTransforms(
141 *static_cast<const TransformArray
*>(aValueToAdd
.mU
.mPtr
));
143 // We should have 0 or 1 transforms in the src list.
144 NS_ASSERTION(srcTransforms
.Length() < 2,
145 "Trying to do sandwich add of more than one value");
147 // The empty src transform list case only occurs in some limited circumstances
148 // where we create an empty 'from' value to interpolate from (e.g.
149 // by-animation) but then skip the interpolation step for some reason (e.g.
150 // because we have an indefinite duration which means we'll never get past the
151 // first value) and instead attempt to add that empty value to the underlying
153 // In any case, the expected result is that nothing is added.
154 if (srcTransforms
.IsEmpty()) return NS_OK
;
156 // Stick the src on the end of the array
157 const SVGTransformSMILData
& srcTransform
= srcTransforms
[0];
158 SVGTransformSMILData
* result
=
159 dstTransforms
.AppendElement(srcTransform
, fallible
);
160 NS_ENSURE_TRUE(result
, NS_ERROR_OUT_OF_MEMORY
);
165 nsresult
SVGTransformListSMILType::ComputeDistance(const SMILValue
& aFrom
,
166 const SMILValue
& aTo
,
167 double& aDistance
) const {
168 MOZ_ASSERT(aFrom
.mType
== aTo
.mType
,
169 "Can't compute difference between different SMIL types");
170 MOZ_ASSERT(aFrom
.mType
== this, "Unexpected SMIL type");
172 const TransformArray
* fromTransforms
=
173 static_cast<const TransformArray
*>(aFrom
.mU
.mPtr
);
174 const TransformArray
* toTransforms
=
175 static_cast<const TransformArray
*>(aTo
.mU
.mPtr
);
177 // ComputeDistance is only used for calculating distances between single
178 // values in a values array which necessarily have the same type
180 // So we should only have one transform in each array and they should be of
182 NS_ASSERTION(fromTransforms
->Length() == 1,
183 "Wrong number of elements in from value");
184 NS_ASSERTION(toTransforms
->Length() == 1,
185 "Wrong number of elements in to value");
187 const SVGTransformSMILData
& fromTransform
= (*fromTransforms
)[0];
188 const SVGTransformSMILData
& toTransform
= (*toTransforms
)[0];
189 NS_ASSERTION(fromTransform
.mTransformType
== toTransform
.mTransformType
,
190 "Incompatible transform types to calculate distance between");
192 switch (fromTransform
.mTransformType
) {
193 // We adopt the SVGT1.2 notions of distance here
194 // See: http://www.w3.org/TR/SVGTiny12/animate.html#complexDistances
195 // (As discussed in bug #469040)
196 case SVG_TRANSFORM_TRANSLATE
:
197 case SVG_TRANSFORM_SCALE
: {
198 const float& a_tx
= fromTransform
.mParams
[0];
199 const float& a_ty
= fromTransform
.mParams
[1];
200 const float& b_tx
= toTransform
.mParams
[0];
201 const float& b_ty
= toTransform
.mParams
[1];
202 aDistance
= sqrt(pow(a_tx
- b_tx
, 2) + (pow(a_ty
- b_ty
, 2)));
205 case SVG_TRANSFORM_ROTATE
:
206 case SVG_TRANSFORM_SKEWX
:
207 case SVG_TRANSFORM_SKEWY
: {
208 const float& a
= fromTransform
.mParams
[0];
209 const float& b
= toTransform
.mParams
[0];
210 aDistance
= std::fabs(a
- b
);
214 NS_ERROR("Got bad transform types for calculating distances");
216 return NS_ERROR_FAILURE
;
222 nsresult
SVGTransformListSMILType::Interpolate(const SMILValue
& aStartVal
,
223 const SMILValue
& aEndVal
,
224 double aUnitDistance
,
225 SMILValue
& aResult
) const {
226 MOZ_ASSERT(aStartVal
.mType
== aEndVal
.mType
,
227 "Can't interpolate between different SMIL types");
228 MOZ_ASSERT(aStartVal
.mType
== this, "Unexpected type for interpolation");
229 MOZ_ASSERT(aResult
.mType
== this, "Unexpected result type");
231 const TransformArray
& startTransforms
=
232 (*static_cast<const TransformArray
*>(aStartVal
.mU
.mPtr
));
233 const TransformArray
& endTransforms(
234 *static_cast<const TransformArray
*>(aEndVal
.mU
.mPtr
));
236 // We may have 0..n transforms in the start transform array (the base
237 // value) but we should only have 1 transform in the end transform array
238 NS_ASSERTION(endTransforms
.Length() == 1,
239 "Invalid end-point for interpolating between transform values");
241 // The end point should never be a matrix transform
242 const SVGTransformSMILData
& endTransform
= endTransforms
[0];
243 NS_ASSERTION(endTransform
.mTransformType
!= SVG_TRANSFORM_MATRIX
,
244 "End point for interpolation should not be a matrix transform");
246 // If we have 0 or more than 1 transform in the start transform array then we
247 // just interpolate from 0, 0, 0
248 // Likewise, even if there's only 1 transform in the start transform array
249 // then if the type of the start transform doesn't match the end then we
250 // can't interpolate and should just use 0, 0, 0
251 static float identityParams
[3] = {0.f
};
252 const float* startParams
= nullptr;
253 if (startTransforms
.Length() == 1) {
254 const SVGTransformSMILData
& startTransform
= startTransforms
[0];
255 if (startTransform
.mTransformType
== endTransform
.mTransformType
) {
256 startParams
= startTransform
.mParams
;
260 startParams
= identityParams
;
263 const float* endParams
= endTransform
.mParams
;
265 // Interpolate between the params
267 for (int i
= 0; i
<= 2; ++i
) {
268 const float& a
= startParams
[i
];
269 const float& b
= endParams
[i
];
270 newParams
[i
] = static_cast<float>(a
+ (b
- a
) * aUnitDistance
);
274 SVGTransformSMILData
resultTransform(endTransform
.mTransformType
, newParams
);
276 // Clear the way for it in the result array
277 TransformArray
& dstTransforms
=
278 (*static_cast<TransformArray
*>(aResult
.mU
.mPtr
));
279 dstTransforms
.Clear();
282 SVGTransformSMILData
* transform
=
283 dstTransforms
.AppendElement(resultTransform
, fallible
);
284 NS_ENSURE_TRUE(transform
, NS_ERROR_OUT_OF_MEMORY
);
289 //----------------------------------------------------------------------
290 // Transform array accessors
293 nsresult
SVGTransformListSMILType::AppendTransform(
294 const SVGTransformSMILData
& aTransform
, SMILValue
& aValue
) {
295 MOZ_ASSERT(aValue
.mType
== Singleton(), "Unexpected SMIL value type");
297 TransformArray
& transforms
= *static_cast<TransformArray
*>(aValue
.mU
.mPtr
);
298 return transforms
.AppendElement(aTransform
, fallible
)
300 : NS_ERROR_OUT_OF_MEMORY
;
304 bool SVGTransformListSMILType::AppendTransforms(const SVGTransformList
& aList
,
306 MOZ_ASSERT(aValue
.mType
== Singleton(), "Unexpected SMIL value type");
308 TransformArray
& transforms
= *static_cast<TransformArray
*>(aValue
.mU
.mPtr
);
310 if (!transforms
.SetCapacity(transforms
.Length() + aList
.Length(), fallible
))
313 for (uint32_t i
= 0; i
< aList
.Length(); ++i
) {
314 // No need to check the return value below since we have already allocated
315 // the necessary space
317 transforms
.AppendElement(SVGTransformSMILData(aList
[i
]), fallible
));
323 bool SVGTransformListSMILType::GetTransforms(
324 const SMILValue
& aValue
, FallibleTArray
<SVGTransform
>& aTransforms
) {
325 MOZ_ASSERT(aValue
.mType
== Singleton(), "Unexpected SMIL value type");
327 const TransformArray
& smilTransforms
=
328 *static_cast<const TransformArray
*>(aValue
.mU
.mPtr
);
331 if (!aTransforms
.SetCapacity(smilTransforms
.Length(), fallible
)) return false;
333 for (const auto& smilTransform
: smilTransforms
) {
334 // No need to check the return value below since we have already allocated
335 // the necessary space
336 (void)aTransforms
.AppendElement(smilTransform
.ToSVGTransform(), fallible
);
341 } // namespace mozilla