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