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 mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Mozilla Corporation
19 : *
20 : * Contributor(s):
21 : * Keith Schwarz <kschwarz@mozilla.com> (original author)
22 : * Matt Woodrow <mwoodrow@mozilla.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 : /*
39 : * A class used for intermediate representations of the -moz-transform property.
40 : */
41 :
42 : #include "nsStyleTransformMatrix.h"
43 : #include "nsAutoPtr.h"
44 : #include "nsCSSValue.h"
45 : #include "nsStyleContext.h"
46 : #include "nsPresContext.h"
47 : #include "nsRuleNode.h"
48 : #include "nsCSSKeywords.h"
49 : #include "nsMathUtils.h"
50 : #include "CSSCalc.h"
51 : #include "nsStyleAnimation.h"
52 :
53 : namespace css = mozilla::css;
54 :
55 : namespace nsStyleTransformMatrix {
56 :
57 : /* Note on floating point precision: The transform matrix is an array
58 : * of single precision 'float's, and so are most of the input values
59 : * we get from the style system, but intermediate calculations
60 : * involving angles need to be done in 'double'.
61 : */
62 :
63 : /* Force small values to zero. We do this to avoid having sin(360deg)
64 : * evaluate to a tiny but nonzero value.
65 : */
66 0 : static double FlushToZero(double aVal)
67 : {
68 0 : if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON)
69 0 : return 0.0f;
70 : else
71 0 : return aVal;
72 : }
73 :
74 : /* Helper function to fill in an nscoord with the specified nsCSSValue. */
75 0 : static nscoord CalcLength(const nsCSSValue &aValue,
76 : nsStyleContext* aContext,
77 : nsPresContext* aPresContext,
78 : bool &aCanStoreInRuleTree)
79 : {
80 0 : if (aValue.GetUnit() == eCSSUnit_Pixel ||
81 0 : aValue.GetUnit() == eCSSUnit_Number) {
82 : // Handle this here (even though nsRuleNode::CalcLength handles it
83 : // fine) so that callers are allowed to pass a null style context
84 : // and pres context to SetToTransformFunction if they know (as
85 : // nsStyleAnimation does) that all lengths within the transform
86 : // function have already been computed to pixels and percents.
87 : //
88 : // Raw numbers are treated as being pixels.
89 0 : return nsPresContext::CSSPixelsToAppUnits(aValue.GetFloatValue());
90 : }
91 : return nsRuleNode::CalcLength(aValue, aContext, aPresContext,
92 0 : aCanStoreInRuleTree);
93 : }
94 :
95 : static float
96 0 : ProcessTranslatePart(const nsCSSValue& aValue,
97 : nsStyleContext* aContext,
98 : nsPresContext* aPresContext,
99 : bool& aCanStoreInRuleTree,
100 : nscoord aSize, float aAppUnitsPerMatrixUnit)
101 : {
102 0 : nscoord offset = 0;
103 0 : float percent = 0.0f;
104 :
105 0 : if (aValue.GetUnit() == eCSSUnit_Percent) {
106 0 : percent = aValue.GetPercentValue();
107 0 : } else if (aValue.IsCalcUnit()) {
108 : nsRuleNode::ComputedCalc result =
109 : nsRuleNode::SpecifiedCalcToComputedCalc(aValue, aContext, aPresContext,
110 0 : aCanStoreInRuleTree);
111 0 : percent = result.mPercent;
112 0 : offset = result.mLength;
113 : } else {
114 : offset = CalcLength(aValue, aContext, aPresContext,
115 0 : aCanStoreInRuleTree);
116 : }
117 :
118 0 : return (percent * NSAppUnitsToFloatPixels(aSize, aAppUnitsPerMatrixUnit)) +
119 0 : NSAppUnitsToFloatPixels(offset, aAppUnitsPerMatrixUnit);
120 : }
121 :
122 : /**
123 : * Helper functions to process all the transformation function types.
124 : *
125 : * These take a matrix parameter to accumulate the current matrix.
126 : */
127 :
128 : /* Helper function to process a matrix entry. */
129 : static void
130 0 : ProcessMatrix(gfx3DMatrix& aMatrix,
131 : const nsCSSValue::Array* aData,
132 : nsStyleContext* aContext,
133 : nsPresContext* aPresContext,
134 : bool& aCanStoreInRuleTree,
135 : nsRect& aBounds, float aAppUnitsPerMatrixUnit)
136 : {
137 0 : NS_PRECONDITION(aData->Count() == 7, "Invalid array!");
138 :
139 0 : gfxMatrix result;
140 :
141 : /* Take the first four elements out of the array as floats and store
142 : * them.
143 : */
144 0 : result.xx = aData->Item(1).GetFloatValue();
145 0 : result.yx = aData->Item(2).GetFloatValue();
146 0 : result.xy = aData->Item(3).GetFloatValue();
147 0 : result.yy = aData->Item(4).GetFloatValue();
148 :
149 : /* The last two elements have their length parts stored in aDelta
150 : * and their percent parts stored in aX[0] and aY[1].
151 : */
152 0 : result.x0 = ProcessTranslatePart(aData->Item(5),
153 : aContext, aPresContext, aCanStoreInRuleTree,
154 0 : aBounds.Width(), aAppUnitsPerMatrixUnit);
155 0 : result.y0 = ProcessTranslatePart(aData->Item(6),
156 : aContext, aPresContext, aCanStoreInRuleTree,
157 0 : aBounds.Height(), aAppUnitsPerMatrixUnit);
158 :
159 0 : aMatrix.PreMultiply(result);
160 0 : }
161 :
162 : static void
163 0 : ProcessMatrix3D(gfx3DMatrix& aMatrix,
164 : const nsCSSValue::Array* aData,
165 : nsStyleContext* aContext,
166 : nsPresContext* aPresContext,
167 : bool& aCanStoreInRuleTree,
168 : nsRect& aBounds, float aAppUnitsPerMatrixUnit)
169 : {
170 0 : NS_PRECONDITION(aData->Count() == 17, "Invalid array!");
171 :
172 0 : gfx3DMatrix temp;
173 :
174 0 : temp._11 = aData->Item(1).GetFloatValue();
175 0 : temp._12 = aData->Item(2).GetFloatValue();
176 0 : temp._13 = aData->Item(3).GetFloatValue();
177 0 : temp._14 = aData->Item(4).GetFloatValue();
178 0 : temp._21 = aData->Item(5).GetFloatValue();
179 0 : temp._22 = aData->Item(6).GetFloatValue();
180 0 : temp._23 = aData->Item(7).GetFloatValue();
181 0 : temp._24 = aData->Item(8).GetFloatValue();
182 0 : temp._31 = aData->Item(9).GetFloatValue();
183 0 : temp._32 = aData->Item(10).GetFloatValue();
184 0 : temp._33 = aData->Item(11).GetFloatValue();
185 0 : temp._34 = aData->Item(12).GetFloatValue();
186 0 : temp._44 = aData->Item(16).GetFloatValue();
187 :
188 0 : temp._41 = ProcessTranslatePart(aData->Item(13),
189 : aContext, aPresContext, aCanStoreInRuleTree,
190 0 : aBounds.Width(), aAppUnitsPerMatrixUnit);
191 0 : temp._42 = ProcessTranslatePart(aData->Item(14),
192 : aContext, aPresContext, aCanStoreInRuleTree,
193 0 : aBounds.Height(), aAppUnitsPerMatrixUnit);
194 0 : temp._43 = ProcessTranslatePart(aData->Item(15),
195 : aContext, aPresContext, aCanStoreInRuleTree,
196 0 : aBounds.Height(), aAppUnitsPerMatrixUnit);
197 :
198 0 : aMatrix.PreMultiply(temp);
199 0 : }
200 :
201 : /* Helper function to process two matrices that we need to interpolate between */
202 : static void
203 0 : ProcessInterpolateMatrix(gfx3DMatrix& aMatrix,
204 : const nsCSSValue::Array* aData,
205 : nsStyleContext* aContext,
206 : nsPresContext* aPresContext,
207 : bool& aCanStoreInRuleTree,
208 : nsRect& aBounds, float aAppUnitsPerMatrixUnit)
209 : {
210 0 : NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
211 :
212 0 : gfx3DMatrix matrix1, matrix2;
213 0 : if (aData->Item(1).GetUnit() == eCSSUnit_List) {
214 0 : matrix1 = nsStyleTransformMatrix::ReadTransforms(aData->Item(1).GetListValue(),
215 : aContext, aPresContext,
216 : aCanStoreInRuleTree,
217 0 : aBounds, aAppUnitsPerMatrixUnit);
218 : }
219 0 : if (aData->Item(2).GetUnit() == eCSSUnit_List) {
220 0 : matrix2 = ReadTransforms(aData->Item(2).GetListValue(),
221 : aContext, aPresContext,
222 : aCanStoreInRuleTree,
223 0 : aBounds, aAppUnitsPerMatrixUnit);
224 : }
225 0 : double progress = aData->Item(3).GetPercentValue();
226 :
227 0 : aMatrix = nsStyleAnimation::InterpolateTransformMatrix(matrix1, matrix2, progress) * aMatrix;
228 0 : }
229 :
230 : /* Helper function to process a translatex function. */
231 : static void
232 0 : ProcessTranslateX(gfx3DMatrix& aMatrix,
233 : const nsCSSValue::Array* aData,
234 : nsStyleContext* aContext,
235 : nsPresContext* aPresContext,
236 : bool& aCanStoreInRuleTree,
237 : nsRect& aBounds, float aAppUnitsPerMatrixUnit)
238 : {
239 0 : NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
240 :
241 0 : gfxPoint3D temp;
242 :
243 0 : temp.x = ProcessTranslatePart(aData->Item(1),
244 : aContext, aPresContext, aCanStoreInRuleTree,
245 0 : aBounds.Width(), aAppUnitsPerMatrixUnit);
246 0 : aMatrix.Translate(temp);
247 0 : }
248 :
249 : /* Helper function to process a translatey function. */
250 : static void
251 0 : ProcessTranslateY(gfx3DMatrix& aMatrix,
252 : const nsCSSValue::Array* aData,
253 : nsStyleContext* aContext,
254 : nsPresContext* aPresContext,
255 : bool& aCanStoreInRuleTree,
256 : nsRect& aBounds, float aAppUnitsPerMatrixUnit)
257 : {
258 0 : NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
259 :
260 0 : gfxPoint3D temp;
261 :
262 0 : temp.y = ProcessTranslatePart(aData->Item(1),
263 : aContext, aPresContext, aCanStoreInRuleTree,
264 0 : aBounds.Height(), aAppUnitsPerMatrixUnit);
265 0 : aMatrix.Translate(temp);
266 0 : }
267 :
268 : static void
269 0 : ProcessTranslateZ(gfx3DMatrix& aMatrix,
270 : const nsCSSValue::Array* aData,
271 : nsStyleContext* aContext,
272 : nsPresContext* aPresContext,
273 : bool& aCanStoreInRuleTree,
274 : float aAppUnitsPerMatrixUnit)
275 : {
276 0 : NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
277 :
278 0 : gfxPoint3D temp;
279 :
280 0 : temp.z = ProcessTranslatePart(aData->Item(1),
281 : aContext, aPresContext, aCanStoreInRuleTree,
282 0 : 0, aAppUnitsPerMatrixUnit);
283 0 : aMatrix.Translate(temp);
284 0 : }
285 :
286 : /* Helper function to process a translate function. */
287 : static void
288 0 : ProcessTranslate(gfx3DMatrix& aMatrix,
289 : const nsCSSValue::Array* aData,
290 : nsStyleContext* aContext,
291 : nsPresContext* aPresContext,
292 : bool& aCanStoreInRuleTree,
293 : nsRect& aBounds, float aAppUnitsPerMatrixUnit)
294 : {
295 0 : NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Invalid array!");
296 :
297 0 : gfxPoint3D temp;
298 :
299 0 : temp.x = ProcessTranslatePart(aData->Item(1),
300 : aContext, aPresContext, aCanStoreInRuleTree,
301 0 : aBounds.Width(), aAppUnitsPerMatrixUnit);
302 :
303 : /* If we read in a Y component, set it appropriately */
304 0 : if (aData->Count() == 3) {
305 0 : temp.y = ProcessTranslatePart(aData->Item(2),
306 : aContext, aPresContext, aCanStoreInRuleTree,
307 0 : aBounds.Height(), aAppUnitsPerMatrixUnit);
308 : }
309 0 : aMatrix.Translate(temp);
310 0 : }
311 :
312 : static void
313 0 : ProcessTranslate3D(gfx3DMatrix& aMatrix,
314 : const nsCSSValue::Array* aData,
315 : nsStyleContext* aContext,
316 : nsPresContext* aPresContext,
317 : bool& aCanStoreInRuleTree,
318 : nsRect& aBounds, float aAppUnitsPerMatrixUnit)
319 : {
320 0 : NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
321 :
322 0 : gfxPoint3D temp;
323 :
324 0 : temp.x = ProcessTranslatePart(aData->Item(1),
325 : aContext, aPresContext, aCanStoreInRuleTree,
326 0 : aBounds.Width(), aAppUnitsPerMatrixUnit);
327 :
328 0 : temp.y = ProcessTranslatePart(aData->Item(2),
329 : aContext, aPresContext, aCanStoreInRuleTree,
330 0 : aBounds.Height(), aAppUnitsPerMatrixUnit);
331 :
332 0 : temp.z = ProcessTranslatePart(aData->Item(3),
333 : aContext, aPresContext, aCanStoreInRuleTree,
334 0 : 0, aAppUnitsPerMatrixUnit);
335 :
336 0 : aMatrix.Translate(temp);
337 0 : }
338 :
339 : /* Helper function to set up a scale matrix. */
340 : static void
341 0 : ProcessScaleHelper(gfx3DMatrix& aMatrix,
342 : float aXScale,
343 : float aYScale,
344 : float aZScale)
345 : {
346 0 : aMatrix.Scale(aXScale, aYScale, aZScale);
347 0 : }
348 :
349 : /* Process a scalex function. */
350 : static void
351 0 : ProcessScaleX(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
352 : {
353 0 : NS_PRECONDITION(aData->Count() == 2, "Bad array!");
354 0 : ProcessScaleHelper(aMatrix, aData->Item(1).GetFloatValue(), 1.0f, 1.0f);
355 0 : }
356 :
357 : /* Process a scaley function. */
358 : static void
359 0 : ProcessScaleY(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
360 : {
361 0 : NS_PRECONDITION(aData->Count() == 2, "Bad array!");
362 0 : ProcessScaleHelper(aMatrix, 1.0f, aData->Item(1).GetFloatValue(), 1.0f);
363 0 : }
364 :
365 : static void
366 0 : ProcessScaleZ(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
367 : {
368 0 : NS_PRECONDITION(aData->Count() == 2, "Bad array!");
369 0 : ProcessScaleHelper(aMatrix, 1.0f, 1.0f, aData->Item(1).GetFloatValue());
370 0 : }
371 :
372 : static void
373 0 : ProcessScale3D(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
374 : {
375 0 : NS_PRECONDITION(aData->Count() == 4, "Bad array!");
376 : ProcessScaleHelper(aMatrix,
377 0 : aData->Item(1).GetFloatValue(),
378 0 : aData->Item(2).GetFloatValue(),
379 0 : aData->Item(3).GetFloatValue());
380 0 : }
381 :
382 : /* Process a scale function. */
383 : static void
384 0 : ProcessScale(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
385 : {
386 0 : NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
387 : /* We either have one element or two. If we have one, it's for both X and Y.
388 : * Otherwise it's one for each.
389 : */
390 0 : const nsCSSValue& scaleX = aData->Item(1);
391 0 : const nsCSSValue& scaleY = (aData->Count() == 2 ? scaleX :
392 0 : aData->Item(2));
393 :
394 : ProcessScaleHelper(aMatrix,
395 : scaleX.GetFloatValue(),
396 : scaleY.GetFloatValue(),
397 0 : 1.0f);
398 0 : }
399 :
400 : /* Helper function that, given a set of angles, constructs the appropriate
401 : * skew matrix.
402 : */
403 : static void
404 0 : ProcessSkewHelper(gfx3DMatrix& aMatrix, double aXAngle, double aYAngle)
405 : {
406 0 : aMatrix.SkewXY(aXAngle, aYAngle);
407 0 : }
408 :
409 : /* Function that converts a skewx transform into a matrix. */
410 : static void
411 0 : ProcessSkewX(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
412 : {
413 0 : NS_ASSERTION(aData->Count() == 2, "Bad array!");
414 0 : ProcessSkewHelper(aMatrix, aData->Item(1).GetAngleValueInRadians(), 0.0);
415 0 : }
416 :
417 : /* Function that converts a skewy transform into a matrix. */
418 : static void
419 0 : ProcessSkewY(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
420 : {
421 0 : NS_ASSERTION(aData->Count() == 2, "Bad array!");
422 0 : ProcessSkewHelper(aMatrix, 0.0, aData->Item(1).GetAngleValueInRadians());
423 0 : }
424 :
425 : /* Function that converts a skew transform into a matrix. */
426 : static void
427 0 : ProcessSkew(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
428 : {
429 0 : NS_ASSERTION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
430 :
431 0 : double xSkew = aData->Item(1).GetAngleValueInRadians();
432 0 : double ySkew = (aData->Count() == 2
433 0 : ? 0.0 : aData->Item(2).GetAngleValueInRadians());
434 :
435 0 : ProcessSkewHelper(aMatrix, xSkew, ySkew);
436 0 : }
437 :
438 : /* Function that converts a rotate transform into a matrix. */
439 : static void
440 0 : ProcessRotateZ(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
441 : {
442 0 : NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
443 0 : double theta = aData->Item(1).GetAngleValueInRadians();
444 0 : aMatrix.RotateZ(theta);
445 0 : }
446 :
447 : static void
448 0 : ProcessRotateX(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
449 : {
450 0 : NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
451 0 : double theta = aData->Item(1).GetAngleValueInRadians();
452 0 : aMatrix.RotateX(theta);
453 0 : }
454 :
455 : static void
456 0 : ProcessRotateY(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
457 : {
458 0 : NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
459 0 : double theta = aData->Item(1).GetAngleValueInRadians();
460 0 : aMatrix.RotateY(theta);
461 0 : }
462 :
463 : static void
464 0 : ProcessRotate3D(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
465 : {
466 0 : NS_PRECONDITION(aData->Count() == 5, "Invalid array!");
467 :
468 : /* We want our matrix to look like this:
469 : * | 1 + (1-cos(angle))*(x*x-1) -z*sin(angle)+(1-cos(angle))*x*y y*sin(angle)+(1-cos(angle))*x*z 0 |
470 : * | z*sin(angle)+(1-cos(angle))*x*y 1 + (1-cos(angle))*(y*y-1) -x*sin(angle)+(1-cos(angle))*y*z 0 |
471 : * | -y*sin(angle)+(1-cos(angle))*x*z x*sin(angle)+(1-cos(angle))*y*z 1 + (1-cos(angle))*(z*z-1) 0 |
472 : * | 0 0 0 1 |
473 : * (see http://www.w3.org/TR/css3-3d-transforms/#transform-functions)
474 : */
475 :
476 : /* The current spec specifies a matrix that rotates in the wrong direction. For now we just negate
477 : * the angle provided to get the correct rotation direction until the spec is updated.
478 : * See bug 704468.
479 : */
480 0 : double theta = -aData->Item(4).GetAngleValueInRadians();
481 0 : float cosTheta = FlushToZero(cos(theta));
482 0 : float sinTheta = FlushToZero(sin(theta));
483 :
484 0 : float x = aData->Item(1).GetFloatValue();
485 0 : float y = aData->Item(2).GetFloatValue();
486 0 : float z = aData->Item(3).GetFloatValue();
487 :
488 : /* Normalize [x,y,z] */
489 0 : float length = sqrt(x*x + y*y + z*z);
490 0 : if (length == 0.0) {
491 0 : return;
492 : }
493 0 : x /= length;
494 0 : y /= length;
495 0 : z /= length;
496 :
497 0 : gfx3DMatrix temp;
498 :
499 : /* Create our matrix */
500 0 : temp._11 = 1 + (1 - cosTheta) * (x * x - 1);
501 0 : temp._12 = -z * sinTheta + (1 - cosTheta) * x * y;
502 0 : temp._13 = y * sinTheta + (1 - cosTheta) * x * z;
503 0 : temp._14 = 0.0f;
504 0 : temp._21 = z * sinTheta + (1 - cosTheta) * x * y;
505 0 : temp._22 = 1 + (1 - cosTheta) * (y * y - 1);
506 0 : temp._23 = -x * sinTheta + (1 - cosTheta) * y * z;
507 0 : temp._24 = 0.0f;
508 0 : temp._31 = -y * sinTheta + (1 - cosTheta) * x * z;
509 0 : temp._32 = x * sinTheta + (1 - cosTheta) * y * z;
510 0 : temp._33 = 1 + (1 - cosTheta) * (z * z - 1);
511 0 : temp._34 = 0.0f;
512 0 : temp._41 = 0.0f;
513 0 : temp._42 = 0.0f;
514 0 : temp._43 = 0.0f;
515 0 : temp._44 = 1.0f;
516 :
517 0 : aMatrix = temp * aMatrix;
518 : }
519 :
520 : static void
521 0 : ProcessPerspective(gfx3DMatrix& aMatrix,
522 : const nsCSSValue::Array* aData,
523 : nsStyleContext *aContext,
524 : nsPresContext *aPresContext,
525 : bool &aCanStoreInRuleTree,
526 : float aAppUnitsPerMatrixUnit)
527 : {
528 0 : NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
529 :
530 0 : float depth = ProcessTranslatePart(aData->Item(1), aContext,
531 : aPresContext, aCanStoreInRuleTree,
532 0 : 0, aAppUnitsPerMatrixUnit);
533 0 : aMatrix.Perspective(depth);
534 0 : }
535 :
536 :
537 : /**
538 : * SetToTransformFunction is essentially a giant switch statement that fans
539 : * out to many smaller helper functions.
540 : */
541 : static void
542 0 : MatrixForTransformFunction(gfx3DMatrix& aMatrix,
543 : const nsCSSValue::Array * aData,
544 : nsStyleContext* aContext,
545 : nsPresContext* aPresContext,
546 : bool& aCanStoreInRuleTree,
547 : nsRect& aBounds,
548 : float aAppUnitsPerMatrixUnit)
549 : {
550 0 : NS_PRECONDITION(aData, "Why did you want to get data from a null array?");
551 : // It's OK if aContext and aPresContext are null if the caller already
552 : // knows that all length units have been converted to pixels (as
553 : // nsStyleAnimation does).
554 :
555 :
556 : /* Get the keyword for the transform. */
557 0 : switch (TransformFunctionOf(aData)) {
558 : case eCSSKeyword_translatex:
559 : ProcessTranslateX(aMatrix, aData, aContext, aPresContext,
560 0 : aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
561 0 : break;
562 : case eCSSKeyword_translatey:
563 : ProcessTranslateY(aMatrix, aData, aContext, aPresContext,
564 0 : aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
565 0 : break;
566 : case eCSSKeyword_translatez:
567 : ProcessTranslateZ(aMatrix, aData, aContext, aPresContext,
568 0 : aCanStoreInRuleTree, aAppUnitsPerMatrixUnit);
569 0 : break;
570 : case eCSSKeyword_translate:
571 : ProcessTranslate(aMatrix, aData, aContext, aPresContext,
572 0 : aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
573 0 : break;
574 : case eCSSKeyword_translate3d:
575 : ProcessTranslate3D(aMatrix, aData, aContext, aPresContext,
576 0 : aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
577 0 : break;
578 : case eCSSKeyword_scalex:
579 0 : ProcessScaleX(aMatrix, aData);
580 0 : break;
581 : case eCSSKeyword_scaley:
582 0 : ProcessScaleY(aMatrix, aData);
583 0 : break;
584 : case eCSSKeyword_scalez:
585 0 : ProcessScaleZ(aMatrix, aData);
586 0 : break;
587 : case eCSSKeyword_scale:
588 0 : ProcessScale(aMatrix, aData);
589 0 : break;
590 : case eCSSKeyword_scale3d:
591 0 : ProcessScale3D(aMatrix, aData);
592 0 : break;
593 : case eCSSKeyword_skewx:
594 0 : ProcessSkewX(aMatrix, aData);
595 0 : break;
596 : case eCSSKeyword_skewy:
597 0 : ProcessSkewY(aMatrix, aData);
598 0 : break;
599 : case eCSSKeyword_skew:
600 0 : ProcessSkew(aMatrix, aData);
601 0 : break;
602 : case eCSSKeyword_rotatex:
603 0 : ProcessRotateX(aMatrix, aData);
604 0 : break;
605 : case eCSSKeyword_rotatey:
606 0 : ProcessRotateY(aMatrix, aData);
607 0 : break;
608 : case eCSSKeyword_rotatez:
609 : case eCSSKeyword_rotate:
610 0 : ProcessRotateZ(aMatrix, aData);
611 0 : break;
612 : case eCSSKeyword_rotate3d:
613 0 : ProcessRotate3D(aMatrix, aData);
614 0 : break;
615 : case eCSSKeyword_matrix:
616 : ProcessMatrix(aMatrix, aData, aContext, aPresContext,
617 0 : aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
618 0 : break;
619 : case eCSSKeyword_matrix3d:
620 : ProcessMatrix3D(aMatrix, aData, aContext, aPresContext,
621 0 : aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
622 0 : break;
623 : case eCSSKeyword_interpolatematrix:
624 : ProcessInterpolateMatrix(aMatrix, aData, aContext, aPresContext,
625 0 : aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
626 0 : break;
627 : case eCSSKeyword_perspective:
628 : ProcessPerspective(aMatrix, aData, aContext, aPresContext,
629 0 : aCanStoreInRuleTree, aAppUnitsPerMatrixUnit);
630 0 : break;
631 : default:
632 0 : NS_NOTREACHED("Unknown transform function!");
633 : }
634 0 : }
635 :
636 : /**
637 : * Return the transform function, as an nsCSSKeyword, for the given
638 : * nsCSSValue::Array from a transform list.
639 : */
640 : nsCSSKeyword
641 0 : TransformFunctionOf(const nsCSSValue::Array* aData)
642 : {
643 0 : nsAutoString keyword;
644 0 : aData->Item(0).GetStringValue(keyword);
645 0 : return nsCSSKeywords::LookupKeyword(keyword);
646 : }
647 :
648 : gfx3DMatrix
649 0 : ReadTransforms(const nsCSSValueList* aList,
650 : nsStyleContext* aContext,
651 : nsPresContext* aPresContext,
652 : bool &aCanStoreInRuleTree,
653 : nsRect& aBounds,
654 : float aAppUnitsPerMatrixUnit)
655 : {
656 0 : gfx3DMatrix result;
657 :
658 0 : for (const nsCSSValueList* curr = aList; curr != nsnull; curr = curr->mNext) {
659 0 : const nsCSSValue &currElem = curr->mValue;
660 0 : NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function,
661 : "Stream should consist solely of functions!");
662 0 : NS_ASSERTION(currElem.GetArrayValue()->Count() >= 1,
663 : "Incoming function is too short!");
664 :
665 : /* Read in a single transform matrix. */
666 0 : MatrixForTransformFunction(result, currElem.GetArrayValue(), aContext,
667 : aPresContext, aCanStoreInRuleTree,
668 0 : aBounds, aAppUnitsPerMatrixUnit);
669 : }
670 :
671 : return result;
672 : }
673 :
674 : } // namespace nsStyleTransformMatrix
|