Line data Source code
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/. */
6 :
7 : #include "SVGTransformListSMILType.h"
8 : #include "SVGTransformList.h"
9 : #include "nsSVGTransform.h"
10 : #include "nsSMILValue.h"
11 : #include "nsCRT.h"
12 : #include <math.h>
13 :
14 : using namespace mozilla;
15 :
16 : typedef FallibleTArray<SVGTransformSMILData> TransformArray;
17 :
18 : //----------------------------------------------------------------------
19 : // nsISMILType implementation
20 :
21 : void
22 0 : SVGTransformListSMILType::Init(nsSMILValue &aValue) const
23 : {
24 0 : NS_PRECONDITION(aValue.IsNull(), "Unexpected value type");
25 :
26 0 : TransformArray* transforms = new TransformArray(1);
27 0 : aValue.mU.mPtr = transforms;
28 0 : aValue.mType = this;
29 0 : }
30 :
31 : void
32 0 : SVGTransformListSMILType::Destroy(nsSMILValue& aValue) const
33 : {
34 0 : NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
35 0 : TransformArray* params = static_cast<TransformArray*>(aValue.mU.mPtr);
36 0 : delete params;
37 0 : aValue.mU.mPtr = nullptr;
38 0 : aValue.mType = nsSMILNullType::Singleton();
39 0 : }
40 :
41 : nsresult
42 0 : SVGTransformListSMILType::Assign(nsSMILValue& aDest,
43 : const nsSMILValue& aSrc) const
44 : {
45 0 : NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
46 0 : NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
47 :
48 : const TransformArray* srcTransforms =
49 0 : static_cast<const TransformArray*>(aSrc.mU.mPtr);
50 0 : TransformArray* dstTransforms = static_cast<TransformArray*>(aDest.mU.mPtr);
51 0 : if (!dstTransforms->Assign(*srcTransforms, fallible)) {
52 0 : return NS_ERROR_OUT_OF_MEMORY;
53 : }
54 :
55 0 : return NS_OK;
56 : }
57 :
58 : bool
59 0 : SVGTransformListSMILType::IsEqual(const nsSMILValue& aLeft,
60 : const nsSMILValue& aRight) const
61 : {
62 0 : NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
63 0 : NS_PRECONDITION(aLeft.mType == this, "Unexpected SMIL type");
64 :
65 : const TransformArray& leftArr
66 0 : (*static_cast<const TransformArray*>(aLeft.mU.mPtr));
67 : const TransformArray& rightArr
68 0 : (*static_cast<const TransformArray*>(aRight.mU.mPtr));
69 :
70 : // If array-lengths don't match, we're trivially non-equal.
71 0 : if (leftArr.Length() != rightArr.Length()) {
72 0 : return false;
73 : }
74 :
75 : // Array-lengths match -- check each array-entry for equality.
76 0 : uint32_t length = leftArr.Length(); // == rightArr->Length(), if we get here
77 0 : for (uint32_t i = 0; i < length; ++i) {
78 0 : if (leftArr[i] != rightArr[i]) {
79 0 : return false;
80 : }
81 : }
82 :
83 : // Found no differences.
84 0 : return true;
85 : }
86 :
87 : nsresult
88 0 : SVGTransformListSMILType::Add(nsSMILValue& aDest,
89 : const nsSMILValue& aValueToAdd,
90 : uint32_t aCount) const
91 : {
92 0 : NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
93 0 : NS_PRECONDITION(aDest.mType == aValueToAdd.mType, "Incompatible SMIL types");
94 :
95 0 : TransformArray& dstTransforms(*static_cast<TransformArray*>(aDest.mU.mPtr));
96 : const TransformArray& srcTransforms
97 0 : (*static_cast<const TransformArray*>(aValueToAdd.mU.mPtr));
98 :
99 : // We're doing a simple add here (as opposed to a sandwich add below).
100 : // We only do this when we're accumulating a repeat result or calculating
101 : // a by-animation value.
102 : //
103 : // In either case we should have 1 transform in the source array.
104 0 : NS_ASSERTION(srcTransforms.Length() == 1,
105 : "Invalid source transform list to add");
106 :
107 : // And we should have 0 or 1 transforms in the dest array.
108 : // (We can have 0 transforms in the case of by-animation when we are
109 : // calculating the by-value as "0 + by". Zero being represented by an
110 : // nsSMILValue with an empty transform array.)
111 0 : NS_ASSERTION(dstTransforms.Length() < 2,
112 : "Invalid dest transform list to add to");
113 :
114 : // Get the individual transforms to add
115 0 : const SVGTransformSMILData& srcTransform = srcTransforms[0];
116 0 : if (dstTransforms.IsEmpty()) {
117 0 : SVGTransformSMILData* result = dstTransforms.AppendElement(
118 0 : SVGTransformSMILData(srcTransform.mTransformType), fallible);
119 0 : NS_ENSURE_TRUE(result,NS_ERROR_OUT_OF_MEMORY);
120 : }
121 0 : SVGTransformSMILData& dstTransform = dstTransforms[0];
122 :
123 : // The types must be the same
124 0 : NS_ASSERTION(srcTransform.mTransformType == dstTransform.mTransformType,
125 : "Trying to perform simple add of different transform types");
126 :
127 : // And it should be impossible that one of them is of matrix type
128 0 : NS_ASSERTION(
129 : srcTransform.mTransformType != SVG_TRANSFORM_MATRIX,
130 : "Trying to perform simple add with matrix transform");
131 :
132 : // Add the parameters
133 0 : for (int i = 0; i <= 2; ++i) {
134 0 : dstTransform.mParams[i] += srcTransform.mParams[i] * aCount;
135 : }
136 :
137 0 : return NS_OK;
138 : }
139 :
140 : nsresult
141 0 : SVGTransformListSMILType::SandwichAdd(nsSMILValue& aDest,
142 : const nsSMILValue& aValueToAdd) const
143 : {
144 0 : NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
145 0 : NS_PRECONDITION(aDest.mType == aValueToAdd.mType, "Incompatible SMIL types");
146 :
147 : // For <animateTransform> a sandwich add means a matrix post-multiplication
148 : // which just means to put the additional transform on the end of the array
149 :
150 0 : TransformArray& dstTransforms(*static_cast<TransformArray*>(aDest.mU.mPtr));
151 : const TransformArray& srcTransforms
152 0 : (*static_cast<const TransformArray*>(aValueToAdd.mU.mPtr));
153 :
154 : // We should have 0 or 1 transforms in the src list.
155 0 : NS_ASSERTION(srcTransforms.Length() < 2,
156 : "Trying to do sandwich add of more than one value");
157 :
158 : // The empty src transform list case only occurs in some limited circumstances
159 : // where we create an empty 'from' value to interpolate from (e.g.
160 : // by-animation) but then skip the interpolation step for some reason (e.g.
161 : // because we have an indefinite duration which means we'll never get past the
162 : // first value) and instead attempt to add that empty value to the underlying
163 : // value.
164 : // In any case, the expected result is that nothing is added.
165 0 : if (srcTransforms.IsEmpty())
166 0 : return NS_OK;
167 :
168 : // Stick the src on the end of the array
169 0 : const SVGTransformSMILData& srcTransform = srcTransforms[0];
170 : SVGTransformSMILData* result =
171 0 : dstTransforms.AppendElement(srcTransform, fallible);
172 0 : NS_ENSURE_TRUE(result,NS_ERROR_OUT_OF_MEMORY);
173 :
174 0 : return NS_OK;
175 : }
176 :
177 : nsresult
178 0 : SVGTransformListSMILType::ComputeDistance(const nsSMILValue& aFrom,
179 : const nsSMILValue& aTo,
180 : double& aDistance) const
181 : {
182 0 : NS_PRECONDITION(aFrom.mType == aTo.mType,
183 : "Can't compute difference between different SMIL types");
184 0 : NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
185 :
186 : const TransformArray* fromTransforms =
187 0 : static_cast<const TransformArray*>(aFrom.mU.mPtr);
188 : const TransformArray* toTransforms =
189 0 : static_cast<const TransformArray*>(aTo.mU.mPtr);
190 :
191 : // ComputeDistance is only used for calculating distances between single
192 : // values in a values array which necessarily have the same type
193 : //
194 : // So we should only have one transform in each array and they should be of
195 : // the same type
196 0 : NS_ASSERTION(fromTransforms->Length() == 1,
197 : "Wrong number of elements in from value");
198 0 : NS_ASSERTION(toTransforms->Length() == 1,
199 : "Wrong number of elements in to value");
200 :
201 0 : const SVGTransformSMILData& fromTransform = (*fromTransforms)[0];
202 0 : const SVGTransformSMILData& toTransform = (*toTransforms)[0];
203 0 : NS_ASSERTION(fromTransform.mTransformType == toTransform.mTransformType,
204 : "Incompatible transform types to calculate distance between");
205 :
206 0 : switch (fromTransform.mTransformType)
207 : {
208 : // We adopt the SVGT1.2 notions of distance here
209 : // See: http://www.w3.org/TR/SVGTiny12/animate.html#complexDistances
210 : // (As discussed in bug #469040)
211 : case SVG_TRANSFORM_TRANSLATE:
212 : case SVG_TRANSFORM_SCALE:
213 : {
214 0 : const float& a_tx = fromTransform.mParams[0];
215 0 : const float& a_ty = fromTransform.mParams[1];
216 0 : const float& b_tx = toTransform.mParams[0];
217 0 : const float& b_ty = toTransform.mParams[1];
218 0 : aDistance = sqrt(pow(a_tx - b_tx, 2) + (pow(a_ty - b_ty, 2)));
219 : }
220 0 : break;
221 :
222 : case SVG_TRANSFORM_ROTATE:
223 : case SVG_TRANSFORM_SKEWX:
224 : case SVG_TRANSFORM_SKEWY:
225 : {
226 0 : const float& a = fromTransform.mParams[0];
227 0 : const float& b = toTransform.mParams[0];
228 0 : aDistance = fabs(a-b);
229 : }
230 0 : break;
231 :
232 : default:
233 0 : NS_ERROR("Got bad transform types for calculating distances");
234 0 : aDistance = 1.0;
235 0 : return NS_ERROR_FAILURE;
236 : }
237 :
238 0 : return NS_OK;
239 : }
240 :
241 : nsresult
242 0 : SVGTransformListSMILType::Interpolate(const nsSMILValue& aStartVal,
243 : const nsSMILValue& aEndVal,
244 : double aUnitDistance,
245 : nsSMILValue& aResult) const
246 : {
247 0 : NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
248 : "Can't interpolate between different SMIL types");
249 0 : NS_PRECONDITION(aStartVal.mType == this,
250 : "Unexpected type for interpolation");
251 0 : NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
252 :
253 : const TransformArray& startTransforms =
254 0 : (*static_cast<const TransformArray*>(aStartVal.mU.mPtr));
255 : const TransformArray& endTransforms
256 0 : (*static_cast<const TransformArray*>(aEndVal.mU.mPtr));
257 :
258 : // We may have 0..n transforms in the start transform array (the base
259 : // value) but we should only have 1 transform in the end transform array
260 0 : NS_ASSERTION(endTransforms.Length() == 1,
261 : "Invalid end-point for interpolating between transform values");
262 :
263 : // The end point should never be a matrix transform
264 0 : const SVGTransformSMILData& endTransform = endTransforms[0];
265 0 : NS_ASSERTION(
266 : endTransform.mTransformType != SVG_TRANSFORM_MATRIX,
267 : "End point for interpolation should not be a matrix transform");
268 :
269 : // If we have 0 or more than 1 transform in the start transform array then we
270 : // just interpolate from 0, 0, 0
271 : // Likewise, even if there's only 1 transform in the start transform array
272 : // then if the type of the start transform doesn't match the end then we
273 : // can't interpolate and should just use 0, 0, 0
274 : static float identityParams[3] = { 0.f };
275 0 : const float* startParams = nullptr;
276 0 : if (startTransforms.Length() == 1) {
277 0 : const SVGTransformSMILData& startTransform = startTransforms[0];
278 0 : if (startTransform.mTransformType == endTransform.mTransformType) {
279 0 : startParams = startTransform.mParams;
280 : }
281 : }
282 0 : if (!startParams) {
283 0 : startParams = identityParams;
284 : }
285 :
286 0 : const float* endParams = endTransform.mParams;
287 :
288 : // Interpolate between the params
289 : float newParams[3];
290 0 : for (int i = 0; i <= 2; ++i) {
291 0 : const float& a = startParams[i];
292 0 : const float& b = endParams[i];
293 0 : newParams[i] = static_cast<float>(a + (b - a) * aUnitDistance);
294 : }
295 :
296 : // Make the result
297 0 : SVGTransformSMILData resultTransform(endTransform.mTransformType, newParams);
298 :
299 : // Clear the way for it in the result array
300 : TransformArray& dstTransforms =
301 0 : (*static_cast<TransformArray*>(aResult.mU.mPtr));
302 0 : dstTransforms.Clear();
303 :
304 : // Assign the result
305 : SVGTransformSMILData* transform =
306 0 : dstTransforms.AppendElement(resultTransform, fallible);
307 0 : NS_ENSURE_TRUE(transform,NS_ERROR_OUT_OF_MEMORY);
308 :
309 0 : return NS_OK;
310 : }
311 :
312 : //----------------------------------------------------------------------
313 : // Transform array accessors
314 :
315 : // static
316 : nsresult
317 0 : SVGTransformListSMILType::AppendTransform(
318 : const SVGTransformSMILData& aTransform,
319 : nsSMILValue& aValue)
320 : {
321 0 : NS_PRECONDITION(aValue.mType == Singleton(), "Unexpected SMIL value type");
322 :
323 0 : TransformArray& transforms = *static_cast<TransformArray*>(aValue.mU.mPtr);
324 0 : return transforms.AppendElement(aTransform, fallible) ?
325 0 : NS_OK : NS_ERROR_OUT_OF_MEMORY;
326 : }
327 :
328 : // static
329 : bool
330 0 : SVGTransformListSMILType::AppendTransforms(const SVGTransformList& aList,
331 : nsSMILValue& aValue)
332 : {
333 0 : NS_PRECONDITION(aValue.mType == Singleton(), "Unexpected SMIL value type");
334 :
335 0 : TransformArray& transforms = *static_cast<TransformArray*>(aValue.mU.mPtr);
336 :
337 0 : if (!transforms.SetCapacity(transforms.Length() + aList.Length(), fallible))
338 0 : return false;
339 :
340 0 : for (uint32_t i = 0; i < aList.Length(); ++i) {
341 : // No need to check the return value below since we have already allocated
342 : // the necessary space
343 0 : MOZ_ALWAYS_TRUE(transforms.AppendElement(SVGTransformSMILData(aList[i]),
344 : fallible));
345 : }
346 0 : return true;
347 : }
348 :
349 : // static
350 : bool
351 0 : SVGTransformListSMILType::GetTransforms(const nsSMILValue& aValue,
352 : FallibleTArray<nsSVGTransform>& aTransforms)
353 : {
354 0 : NS_PRECONDITION(aValue.mType == Singleton(), "Unexpected SMIL value type");
355 :
356 : const TransformArray& smilTransforms =
357 0 : *static_cast<const TransformArray*>(aValue.mU.mPtr);
358 :
359 0 : aTransforms.Clear();
360 0 : if (!aTransforms.SetCapacity(smilTransforms.Length(), fallible))
361 0 : return false;
362 :
363 0 : for (uint32_t i = 0; i < smilTransforms.Length(); ++i) {
364 : // No need to check the return value below since we have already allocated
365 : // the necessary space
366 0 : aTransforms.AppendElement(smilTransforms[i].ToSVGTransform(), fallible);
367 : }
368 0 : return true;
369 : }
|