123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- /*
- * A class used for intermediate representations of the -moz-transform property.
- */
- #include "nsStyleTransformMatrix.h"
- #include "nsCSSValue.h"
- #include "nsLayoutUtils.h"
- #include "nsPresContext.h"
- #include "nsRuleNode.h"
- #include "nsSVGUtils.h"
- #include "nsCSSKeywords.h"
- #include "mozilla/StyleAnimationValue.h"
- #include "gfxMatrix.h"
- #include "gfxQuaternion.h"
- using namespace mozilla;
- using namespace mozilla::gfx;
- namespace nsStyleTransformMatrix {
- /* Note on floating point precision: The transform matrix is an array
- * of single precision 'float's, and so are most of the input values
- * we get from the style system, but intermediate calculations
- * involving angles need to be done in 'double'.
- */
- // Define UNIFIED_CONTINUATIONS here and in nsDisplayList.cpp
- // to have the transform property try
- // to transform content with continuations as one unified block instead of
- // several smaller ones. This is currently disabled because it doesn't work
- // correctly, since when the frames are initially being reflowed, their
- // continuations all compute their bounding rects independently of each other
- // and consequently get the wrong value.
- //#define UNIFIED_CONTINUATIONS
- void
- TransformReferenceBox::EnsureDimensionsAreCached()
- {
- if (mIsCached) {
- return;
- }
- MOZ_ASSERT(mFrame);
- mIsCached = true;
- if (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
- if (!nsLayoutUtils::SVGTransformBoxEnabled()) {
- mX = -mFrame->GetPosition().x;
- mY = -mFrame->GetPosition().y;
- Size contextSize = nsSVGUtils::GetContextSize(mFrame);
- mWidth = nsPresContext::CSSPixelsToAppUnits(contextSize.width);
- mHeight = nsPresContext::CSSPixelsToAppUnits(contextSize.height);
- } else
- if (mFrame->StyleDisplay()->mTransformBox ==
- NS_STYLE_TRANSFORM_BOX_FILL_BOX) {
- // Percentages in transforms resolve against the SVG bbox, and the
- // transform is relative to the top-left of the SVG bbox.
- gfxRect bbox = nsSVGUtils::GetBBox(const_cast<nsIFrame*>(mFrame));
- nsRect bboxInAppUnits =
- nsLayoutUtils::RoundGfxRectToAppRect(bbox,
- mFrame->PresContext()->AppUnitsPerCSSPixel());
- // The mRect of an SVG nsIFrame is its user space bounds *including*
- // stroke and markers, whereas bboxInAppUnits is its user space bounds
- // including fill only. We need to note the offset of the reference box
- // from the frame's mRect in mX/mY.
- mX = bboxInAppUnits.x - mFrame->GetPosition().x;
- mY = bboxInAppUnits.y - mFrame->GetPosition().y;
- mWidth = bboxInAppUnits.width;
- mHeight = bboxInAppUnits.height;
- } else {
- // The value 'border-box' is treated as 'view-box' for SVG content.
- MOZ_ASSERT(mFrame->StyleDisplay()->mTransformBox ==
- NS_STYLE_TRANSFORM_BOX_VIEW_BOX ||
- mFrame->StyleDisplay()->mTransformBox ==
- NS_STYLE_TRANSFORM_BOX_BORDER_BOX,
- "Unexpected value for 'transform-box'");
- // Percentages in transforms resolve against the width/height of the
- // nearest viewport (or its viewBox if one is applied), and the
- // transform is relative to {0,0} in current user space.
- mX = -mFrame->GetPosition().x;
- mY = -mFrame->GetPosition().y;
- Size contextSize = nsSVGUtils::GetContextSize(mFrame);
- mWidth = nsPresContext::CSSPixelsToAppUnits(contextSize.width);
- mHeight = nsPresContext::CSSPixelsToAppUnits(contextSize.height);
- }
- return;
- }
- // If UNIFIED_CONTINUATIONS is not defined, this is simply the frame's
- // bounding rectangle, translated to the origin. Otherwise, it is the
- // smallest rectangle containing a frame and all of its continuations. For
- // example, if there is a <span> element with several continuations split
- // over several lines, this function will return the rectangle containing all
- // of those continuations.
- nsRect rect;
- #ifndef UNIFIED_CONTINUATIONS
- rect = mFrame->GetRect();
- #else
- // Iterate the continuation list, unioning together the bounding rects:
- for (const nsIFrame *currFrame = mFrame->FirstContinuation();
- currFrame != nullptr;
- currFrame = currFrame->GetNextContinuation())
- {
- // Get the frame rect in local coordinates, then translate back to the
- // original coordinates:
- rect.UnionRect(result, nsRect(currFrame->GetOffsetTo(mFrame),
- currFrame->GetSize()));
- }
- #endif
- mX = 0;
- mY = 0;
- mWidth = rect.Width();
- mHeight = rect.Height();
- }
- void
- TransformReferenceBox::Init(const nsSize& aDimensions)
- {
- MOZ_ASSERT(!mFrame && !mIsCached);
- mX = 0;
- mY = 0;
- mWidth = aDimensions.width;
- mHeight = aDimensions.height;
- mIsCached = true;
- }
- float
- ProcessTranslatePart(const nsCSSValue& aValue,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox* aRefBox,
- TransformReferenceBox::DimensionGetter aDimensionGetter)
- {
- nscoord offset = 0;
- float percent = 0.0f;
- if (aValue.GetUnit() == eCSSUnit_Percent) {
- percent = aValue.GetPercentValue();
- } else if (aValue.GetUnit() == eCSSUnit_Pixel ||
- aValue.GetUnit() == eCSSUnit_Number) {
- // Handle this here (even though nsRuleNode::CalcLength handles it
- // fine) so that callers are allowed to pass a null style context
- // and pres context to SetToTransformFunction if they know (as
- // StyleAnimationValue does) that all lengths within the transform
- // function have already been computed to pixels and percents.
- //
- // Raw numbers are treated as being pixels.
- //
- // Don't convert to aValue to AppUnits here to avoid precision issues.
- return aValue.GetFloatValue();
- } else if (aValue.IsCalcUnit()) {
- nsRuleNode::ComputedCalc result =
- nsRuleNode::SpecifiedCalcToComputedCalc(aValue, aContext, aPresContext,
- aConditions);
- percent = result.mPercent;
- offset = result.mLength;
- } else {
- offset = nsRuleNode::CalcLength(aValue, aContext, aPresContext,
- aConditions);
- }
- float translation = NSAppUnitsToFloatPixels(offset,
- nsPresContext::AppUnitsPerCSSPixel());
- // We want to avoid calling aDimensionGetter if there's no percentage to be
- // resolved (for performance reasons - see TransformReferenceBox).
- if (percent != 0.0f && aRefBox && !aRefBox->IsEmpty()) {
- translation += percent *
- NSAppUnitsToFloatPixels((aRefBox->*aDimensionGetter)(),
- nsPresContext::AppUnitsPerCSSPixel());
- }
- return translation;
- }
- /**
- * Helper functions to process all the transformation function types.
- *
- * These take a matrix parameter to accumulate the current matrix.
- */
- /* Helper function to process a matrix entry. */
- static void
- ProcessMatrix(Matrix4x4& aMatrix,
- const nsCSSValue::Array* aData,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox& aRefBox)
- {
- NS_PRECONDITION(aData->Count() == 7, "Invalid array!");
- gfxMatrix result;
- /* Take the first four elements out of the array as floats and store
- * them.
- */
- result._11 = aData->Item(1).GetFloatValue();
- result._12 = aData->Item(2).GetFloatValue();
- result._21 = aData->Item(3).GetFloatValue();
- result._22 = aData->Item(4).GetFloatValue();
- /* The last two elements have their length parts stored in aDelta
- * and their percent parts stored in aX[0] and aY[1].
- */
- result._31 = ProcessTranslatePart(aData->Item(5),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Width);
- result._32 = ProcessTranslatePart(aData->Item(6),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Height);
- aMatrix = result * aMatrix;
- }
- static void
- ProcessMatrix3D(Matrix4x4& aMatrix,
- const nsCSSValue::Array* aData,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox& aRefBox)
- {
- NS_PRECONDITION(aData->Count() == 17, "Invalid array!");
- Matrix4x4 temp;
- temp._11 = aData->Item(1).GetFloatValue();
- temp._12 = aData->Item(2).GetFloatValue();
- temp._13 = aData->Item(3).GetFloatValue();
- temp._14 = aData->Item(4).GetFloatValue();
- temp._21 = aData->Item(5).GetFloatValue();
- temp._22 = aData->Item(6).GetFloatValue();
- temp._23 = aData->Item(7).GetFloatValue();
- temp._24 = aData->Item(8).GetFloatValue();
- temp._31 = aData->Item(9).GetFloatValue();
- temp._32 = aData->Item(10).GetFloatValue();
- temp._33 = aData->Item(11).GetFloatValue();
- temp._34 = aData->Item(12).GetFloatValue();
- temp._44 = aData->Item(16).GetFloatValue();
- temp._41 = ProcessTranslatePart(aData->Item(13),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Width);
- temp._42 = ProcessTranslatePart(aData->Item(14),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Height);
- temp._43 = ProcessTranslatePart(aData->Item(15),
- aContext, aPresContext, aConditions,
- nullptr);
- aMatrix = temp * aMatrix;
- }
- /* Helper function to process two matrices that we need to interpolate between */
- void
- ProcessInterpolateMatrix(Matrix4x4& aMatrix,
- const nsCSSValue::Array* aData,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox& aRefBox,
- bool* aContains3dTransform)
- {
- NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
- Matrix4x4 matrix1, matrix2;
- if (aData->Item(1).GetUnit() == eCSSUnit_List) {
- matrix1 = nsStyleTransformMatrix::ReadTransforms(aData->Item(1).GetListValue(),
- aContext, aPresContext,
- aConditions,
- aRefBox, nsPresContext::AppUnitsPerCSSPixel(),
- aContains3dTransform);
- }
- if (aData->Item(2).GetUnit() == eCSSUnit_List) {
- matrix2 = ReadTransforms(aData->Item(2).GetListValue(),
- aContext, aPresContext,
- aConditions,
- aRefBox, nsPresContext::AppUnitsPerCSSPixel(),
- aContains3dTransform);
- }
- double progress = aData->Item(3).GetPercentValue();
- aMatrix =
- StyleAnimationValue::InterpolateTransformMatrix(matrix1, matrix2, progress)
- * aMatrix;
- }
- /* Helper function to process a translatex function. */
- static void
- ProcessTranslateX(Matrix4x4& aMatrix,
- const nsCSSValue::Array* aData,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox& aRefBox)
- {
- NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
- Point3D temp;
- temp.x = ProcessTranslatePart(aData->Item(1),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Width);
- aMatrix.PreTranslate(temp);
- }
- /* Helper function to process a translatey function. */
- static void
- ProcessTranslateY(Matrix4x4& aMatrix,
- const nsCSSValue::Array* aData,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox& aRefBox)
- {
- NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
- Point3D temp;
- temp.y = ProcessTranslatePart(aData->Item(1),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Height);
- aMatrix.PreTranslate(temp);
- }
- static void
- ProcessTranslateZ(Matrix4x4& aMatrix,
- const nsCSSValue::Array* aData,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions)
- {
- NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
- Point3D temp;
- temp.z = ProcessTranslatePart(aData->Item(1), aContext,
- aPresContext, aConditions,
- nullptr);
- aMatrix.PreTranslate(temp);
- }
- /* Helper function to process a translate function. */
- static void
- ProcessTranslate(Matrix4x4& aMatrix,
- const nsCSSValue::Array* aData,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox& aRefBox)
- {
- NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Invalid array!");
- Point3D temp;
- temp.x = ProcessTranslatePart(aData->Item(1),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Width);
- /* If we read in a Y component, set it appropriately */
- if (aData->Count() == 3) {
- temp.y = ProcessTranslatePart(aData->Item(2),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Height);
- }
- aMatrix.PreTranslate(temp);
- }
- static void
- ProcessTranslate3D(Matrix4x4& aMatrix,
- const nsCSSValue::Array* aData,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox& aRefBox)
- {
- NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
- Point3D temp;
- temp.x = ProcessTranslatePart(aData->Item(1),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Width);
- temp.y = ProcessTranslatePart(aData->Item(2),
- aContext, aPresContext, aConditions,
- &aRefBox, &TransformReferenceBox::Height);
- temp.z = ProcessTranslatePart(aData->Item(3),
- aContext, aPresContext, aConditions,
- nullptr);
- aMatrix.PreTranslate(temp);
- }
- /* Helper function to set up a scale matrix. */
- static void
- ProcessScaleHelper(Matrix4x4& aMatrix,
- float aXScale,
- float aYScale,
- float aZScale)
- {
- aMatrix.PreScale(aXScale, aYScale, aZScale);
- }
- /* Process a scalex function. */
- static void
- ProcessScaleX(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_PRECONDITION(aData->Count() == 2, "Bad array!");
- ProcessScaleHelper(aMatrix, aData->Item(1).GetFloatValue(), 1.0f, 1.0f);
- }
- /* Process a scaley function. */
- static void
- ProcessScaleY(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_PRECONDITION(aData->Count() == 2, "Bad array!");
- ProcessScaleHelper(aMatrix, 1.0f, aData->Item(1).GetFloatValue(), 1.0f);
- }
- static void
- ProcessScaleZ(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_PRECONDITION(aData->Count() == 2, "Bad array!");
- ProcessScaleHelper(aMatrix, 1.0f, 1.0f, aData->Item(1).GetFloatValue());
- }
- static void
- ProcessScale3D(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_PRECONDITION(aData->Count() == 4, "Bad array!");
- ProcessScaleHelper(aMatrix,
- aData->Item(1).GetFloatValue(),
- aData->Item(2).GetFloatValue(),
- aData->Item(3).GetFloatValue());
- }
- /* Process a scale function. */
- static void
- ProcessScale(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
- /* We either have one element or two. If we have one, it's for both X and Y.
- * Otherwise it's one for each.
- */
- const nsCSSValue& scaleX = aData->Item(1);
- const nsCSSValue& scaleY = (aData->Count() == 2 ? scaleX :
- aData->Item(2));
- ProcessScaleHelper(aMatrix,
- scaleX.GetFloatValue(),
- scaleY.GetFloatValue(),
- 1.0f);
- }
- /* Helper function that, given a set of angles, constructs the appropriate
- * skew matrix.
- */
- static void
- ProcessSkewHelper(Matrix4x4& aMatrix, double aXAngle, double aYAngle)
- {
- aMatrix.SkewXY(aXAngle, aYAngle);
- }
- /* Function that converts a skewx transform into a matrix. */
- static void
- ProcessSkewX(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_ASSERTION(aData->Count() == 2, "Bad array!");
- ProcessSkewHelper(aMatrix, aData->Item(1).GetAngleValueInRadians(), 0.0);
- }
- /* Function that converts a skewy transform into a matrix. */
- static void
- ProcessSkewY(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_ASSERTION(aData->Count() == 2, "Bad array!");
- ProcessSkewHelper(aMatrix, 0.0, aData->Item(1).GetAngleValueInRadians());
- }
- /* Function that converts a skew transform into a matrix. */
- static void
- ProcessSkew(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_ASSERTION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
- double xSkew = aData->Item(1).GetAngleValueInRadians();
- double ySkew = (aData->Count() == 2
- ? 0.0 : aData->Item(2).GetAngleValueInRadians());
- ProcessSkewHelper(aMatrix, xSkew, ySkew);
- }
- /* Function that converts a rotate transform into a matrix. */
- static void
- ProcessRotateZ(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
- double theta = aData->Item(1).GetAngleValueInRadians();
- aMatrix.RotateZ(theta);
- }
- static void
- ProcessRotateX(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
- double theta = aData->Item(1).GetAngleValueInRadians();
- aMatrix.RotateX(theta);
- }
- static void
- ProcessRotateY(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
- double theta = aData->Item(1).GetAngleValueInRadians();
- aMatrix.RotateY(theta);
- }
- static void
- ProcessRotate3D(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
- {
- NS_PRECONDITION(aData->Count() == 5, "Invalid array!");
- double theta = aData->Item(4).GetAngleValueInRadians();
- float x = aData->Item(1).GetFloatValue();
- float y = aData->Item(2).GetFloatValue();
- float z = aData->Item(3).GetFloatValue();
- Matrix4x4 temp;
- temp.SetRotateAxisAngle(x, y, z, theta);
- aMatrix = temp * aMatrix;
- }
- static void
- ProcessPerspective(Matrix4x4& aMatrix,
- const nsCSSValue::Array* aData,
- nsStyleContext *aContext,
- nsPresContext *aPresContext,
- RuleNodeCacheConditions& aConditions)
- {
- NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
- float depth = ProcessTranslatePart(aData->Item(1), aContext,
- aPresContext, aConditions, nullptr);
- ApplyPerspectiveToMatrix(aMatrix, depth);
- }
- /**
- * SetToTransformFunction is essentially a giant switch statement that fans
- * out to many smaller helper functions.
- */
- static void
- MatrixForTransformFunction(Matrix4x4& aMatrix,
- const nsCSSValue::Array * aData,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox& aRefBox,
- bool* aContains3dTransform)
- {
- MOZ_ASSERT(aContains3dTransform);
- NS_PRECONDITION(aData, "Why did you want to get data from a null array?");
- // It's OK if aContext and aPresContext are null if the caller already
- // knows that all length units have been converted to pixels (as
- // StyleAnimationValue does).
- /* Get the keyword for the transform. */
- switch (TransformFunctionOf(aData)) {
- case eCSSKeyword_translatex:
- ProcessTranslateX(aMatrix, aData, aContext, aPresContext,
- aConditions, aRefBox);
- break;
- case eCSSKeyword_translatey:
- ProcessTranslateY(aMatrix, aData, aContext, aPresContext,
- aConditions, aRefBox);
- break;
- case eCSSKeyword_translatez:
- *aContains3dTransform = true;
- ProcessTranslateZ(aMatrix, aData, aContext, aPresContext,
- aConditions);
- break;
- case eCSSKeyword_translate:
- ProcessTranslate(aMatrix, aData, aContext, aPresContext,
- aConditions, aRefBox);
- break;
- case eCSSKeyword_translate3d:
- *aContains3dTransform = true;
- ProcessTranslate3D(aMatrix, aData, aContext, aPresContext,
- aConditions, aRefBox);
- break;
- case eCSSKeyword_scalex:
- ProcessScaleX(aMatrix, aData);
- break;
- case eCSSKeyword_scaley:
- ProcessScaleY(aMatrix, aData);
- break;
- case eCSSKeyword_scalez:
- *aContains3dTransform = true;
- ProcessScaleZ(aMatrix, aData);
- break;
- case eCSSKeyword_scale:
- ProcessScale(aMatrix, aData);
- break;
- case eCSSKeyword_scale3d:
- *aContains3dTransform = true;
- ProcessScale3D(aMatrix, aData);
- break;
- case eCSSKeyword_skewx:
- ProcessSkewX(aMatrix, aData);
- break;
- case eCSSKeyword_skewy:
- ProcessSkewY(aMatrix, aData);
- break;
- case eCSSKeyword_skew:
- ProcessSkew(aMatrix, aData);
- break;
- case eCSSKeyword_rotatex:
- *aContains3dTransform = true;
- ProcessRotateX(aMatrix, aData);
- break;
- case eCSSKeyword_rotatey:
- *aContains3dTransform = true;
- ProcessRotateY(aMatrix, aData);
- break;
- case eCSSKeyword_rotatez:
- *aContains3dTransform = true;
- MOZ_FALLTHROUGH;
- case eCSSKeyword_rotate:
- ProcessRotateZ(aMatrix, aData);
- break;
- case eCSSKeyword_rotate3d:
- *aContains3dTransform = true;
- ProcessRotate3D(aMatrix, aData);
- break;
- case eCSSKeyword_matrix:
- ProcessMatrix(aMatrix, aData, aContext, aPresContext,
- aConditions, aRefBox);
- break;
- case eCSSKeyword_matrix3d:
- *aContains3dTransform = true;
- ProcessMatrix3D(aMatrix, aData, aContext, aPresContext,
- aConditions, aRefBox);
- break;
- case eCSSKeyword_interpolatematrix:
- ProcessInterpolateMatrix(aMatrix, aData, aContext, aPresContext,
- aConditions, aRefBox,
- aContains3dTransform);
- break;
- case eCSSKeyword_perspective:
- *aContains3dTransform = true;
- ProcessPerspective(aMatrix, aData, aContext, aPresContext,
- aConditions);
- break;
- default:
- NS_NOTREACHED("Unknown transform function!");
- }
- }
- /**
- * Return the transform function, as an nsCSSKeyword, for the given
- * nsCSSValue::Array from a transform list.
- */
- nsCSSKeyword
- TransformFunctionOf(const nsCSSValue::Array* aData)
- {
- MOZ_ASSERT(aData->Item(0).GetUnit() == eCSSUnit_Enumerated);
- return aData->Item(0).GetKeywordValue();
- }
- void
- SetIdentityMatrix(nsCSSValue::Array* aMatrix)
- {
- MOZ_ASSERT(aMatrix, "aMatrix should be non-null");
- nsCSSKeyword tfunc = TransformFunctionOf(aMatrix);
- MOZ_ASSERT(tfunc == eCSSKeyword_matrix ||
- tfunc == eCSSKeyword_matrix3d,
- "Only accept matrix and matrix3d");
- if (tfunc == eCSSKeyword_matrix) {
- MOZ_ASSERT(aMatrix->Count() == 7, "Invalid matrix");
- Matrix m;
- for (size_t i = 0; i < 6; ++i) {
- aMatrix->Item(i + 1).SetFloatValue(m.components[i], eCSSUnit_Number);
- }
- return;
- }
- MOZ_ASSERT(aMatrix->Count() == 17, "Invalid matrix3d");
- Matrix4x4 m;
- for (size_t i = 0; i < 16; ++i) {
- aMatrix->Item(i + 1).SetFloatValue(m.components[i], eCSSUnit_Number);
- }
- }
- Matrix4x4
- ReadTransforms(const nsCSSValueList* aList,
- nsStyleContext* aContext,
- nsPresContext* aPresContext,
- RuleNodeCacheConditions& aConditions,
- TransformReferenceBox& aRefBox,
- float aAppUnitsPerMatrixUnit,
- bool* aContains3dTransform)
- {
- Matrix4x4 result;
- for (const nsCSSValueList* curr = aList; curr != nullptr; curr = curr->mNext) {
- const nsCSSValue &currElem = curr->mValue;
- if (currElem.GetUnit() != eCSSUnit_Function) {
- NS_ASSERTION(currElem.GetUnit() == eCSSUnit_None &&
- !aList->mNext,
- "stream should either be a list of functions or a "
- "lone None");
- continue;
- }
- NS_ASSERTION(currElem.GetArrayValue()->Count() >= 1,
- "Incoming function is too short!");
- /* Read in a single transform matrix. */
- MatrixForTransformFunction(result, currElem.GetArrayValue(), aContext,
- aPresContext, aConditions, aRefBox,
- aContains3dTransform);
- }
- float scale = float(nsPresContext::AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;
- result.PreScale(1/scale, 1/scale, 1/scale);
- result.PostScale(scale, scale, scale);
- return result;
- }
- /*
- * The relevant section of the transitions specification:
- * http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
- * defers all of the details to the 2-D and 3-D transforms specifications.
- * For the 2-D transforms specification (all that's relevant for us, right
- * now), the relevant section is:
- * http://dev.w3.org/csswg/css3-2d-transforms/#animation
- * This, in turn, refers to the unmatrix program in Graphics Gems,
- * available from http://tog.acm.org/resources/GraphicsGems/ , and in
- * particular as the file GraphicsGems/gemsii/unmatrix.c
- * in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
- *
- * The unmatrix reference is for general 3-D transform matrices (any of the
- * 16 components can have any value).
- *
- * For CSS 2-D transforms, we have a 2-D matrix with the bottom row constant:
- *
- * [ A C E ]
- * [ B D F ]
- * [ 0 0 1 ]
- *
- * For that case, I believe the algorithm in unmatrix reduces to:
- *
- * (1) If A * D - B * C == 0, the matrix is singular. Fail.
- *
- * (2) Set translation components (Tx and Ty) to the translation parts of
- * the matrix (E and F) and then ignore them for the rest of the time.
- * (For us, E and F each actually consist of three constants: a
- * length, a multiplier for the width, and a multiplier for the
- * height. This actually requires its own decomposition, but I'll
- * keep that separate.)
- *
- * (3) Let the X scale (Sx) be sqrt(A^2 + B^2). Then divide both A and B
- * by it.
- *
- * (4) Let the XY shear (K) be A * C + B * D. From C, subtract A times
- * the XY shear. From D, subtract B times the XY shear.
- *
- * (5) Let the Y scale (Sy) be sqrt(C^2 + D^2). Divide C, D, and the XY
- * shear (K) by it.
- *
- * (6) At this point, A * D - B * C is either 1 or -1. If it is -1,
- * negate the XY shear (K), the X scale (Sx), and A, B, C, and D.
- * (Alternatively, we could negate the XY shear (K) and the Y scale
- * (Sy).)
- *
- * (7) Let the rotation be R = atan2(B, A).
- *
- * Then the resulting decomposed transformation is:
- *
- * translate(Tx, Ty) rotate(R) skewX(atan(K)) scale(Sx, Sy)
- *
- * An interesting result of this is that all of the simple transform
- * functions (i.e., all functions other than matrix()), in isolation,
- * decompose back to themselves except for:
- * 'skewY(φ)', which is 'matrix(1, tan(φ), 0, 1, 0, 0)', which decomposes
- * to 'rotate(φ) skewX(φ) scale(sec(φ), cos(φ))' since (ignoring the
- * alternate sign possibilities that would get fixed in step 6):
- * In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
- * Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
- * In step 4, the XY shear is sin(φ).
- * Thus, after step 4, C = -cos(φ)sin(φ) and D = 1 - sin²(φ) = cos²(φ).
- * Thus, in step 5, the Y scale is sqrt(cos²(φ)(sin²(φ) + cos²(φ)) = cos(φ).
- * Thus, after step 5, C = -sin(φ), D = cos(φ), and the XY shear is tan(φ).
- * Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
- * In step 7, the rotation is thus φ.
- *
- * skew(θ, φ), which is matrix(1, tan(φ), tan(θ), 1, 0, 0), which decomposes
- * to 'rotate(φ) skewX(θ + φ) scale(sec(φ), cos(φ))' since (ignoring
- * the alternate sign possibilities that would get fixed in step 6):
- * In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
- * Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
- * In step 4, the XY shear is cos(φ)tan(θ) + sin(φ).
- * Thus, after step 4,
- * C = tan(θ) - cos(φ)(cos(φ)tan(θ) + sin(φ)) = tan(θ)sin²(φ) - cos(φ)sin(φ)
- * D = 1 - sin(φ)(cos(φ)tan(θ) + sin(φ)) = cos²(φ) - sin(φ)cos(φ)tan(θ)
- * Thus, in step 5, the Y scale is sqrt(C² + D²) =
- * sqrt(tan²(θ)(sin⁴(φ) + sin²(φ)cos²(φ)) -
- * 2 tan(θ)(sin³(φ)cos(φ) + sin(φ)cos³(φ)) +
- * (sin²(φ)cos²(φ) + cos⁴(φ))) =
- * sqrt(tan²(θ)sin²(φ) - 2 tan(θ)sin(φ)cos(φ) + cos²(φ)) =
- * cos(φ) - tan(θ)sin(φ) (taking the negative of the obvious solution so
- * we avoid flipping in step 6).
- * After step 5, C = -sin(φ) and D = cos(φ), and the XY shear is
- * (cos(φ)tan(θ) + sin(φ)) / (cos(φ) - tan(θ)sin(φ)) =
- * (dividing both numerator and denominator by cos(φ))
- * (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)) = tan(θ + φ).
- * (See http://en.wikipedia.org/wiki/List_of_trigonometric_identities .)
- * Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
- * In step 7, the rotation is thus φ.
- *
- * To check this result, we can multiply things back together:
- *
- * [ cos(φ) -sin(φ) ] [ 1 tan(θ + φ) ] [ sec(φ) 0 ]
- * [ sin(φ) cos(φ) ] [ 0 1 ] [ 0 cos(φ) ]
- *
- * [ cos(φ) cos(φ)tan(θ + φ) - sin(φ) ] [ sec(φ) 0 ]
- * [ sin(φ) sin(φ)tan(θ + φ) + cos(φ) ] [ 0 cos(φ) ]
- *
- * but since tan(θ + φ) = (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)),
- * cos(φ)tan(θ + φ) - sin(φ)
- * = cos(φ)(tan(θ) + tan(φ)) - sin(φ) + sin(φ)tan(θ)tan(φ)
- * = cos(φ)tan(θ) + sin(φ) - sin(φ) + sin(φ)tan(θ)tan(φ)
- * = cos(φ)tan(θ) + sin(φ)tan(θ)tan(φ)
- * = tan(θ) (cos(φ) + sin(φ)tan(φ))
- * = tan(θ) sec(φ) (cos²(φ) + sin²(φ))
- * = tan(θ) sec(φ)
- * and
- * sin(φ)tan(θ + φ) + cos(φ)
- * = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ)
- * = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ)
- * = sec(φ) (sin²(φ) + cos²(φ))
- * = sec(φ)
- * so the above is:
- * [ cos(φ) tan(θ) sec(φ) ] [ sec(φ) 0 ]
- * [ sin(φ) sec(φ) ] [ 0 cos(φ) ]
- *
- * [ 1 tan(θ) ]
- * [ tan(φ) 1 ]
- */
- /*
- * Decompose2DMatrix implements the above decomposition algorithm.
- */
- bool
- Decompose2DMatrix(const Matrix& aMatrix,
- Point3D& aScale,
- ShearArray& aShear,
- gfxQuaternion& aRotate,
- Point3D& aTranslate)
- {
- float A = aMatrix._11,
- B = aMatrix._12,
- C = aMatrix._21,
- D = aMatrix._22;
- if (A * D == B * C) {
- // singular matrix
- return false;
- }
- float scaleX = sqrt(A * A + B * B);
- A /= scaleX;
- B /= scaleX;
- float XYshear = A * C + B * D;
- C -= A * XYshear;
- D -= B * XYshear;
- float scaleY = sqrt(C * C + D * D);
- C /= scaleY;
- D /= scaleY;
- XYshear /= scaleY;
- // A*D - B*C should now be 1 or -1
- NS_ASSERTION(0.99 < Abs(A*D - B*C) && Abs(A*D - B*C) < 1.01,
- "determinant should now be 1 or -1");
- if (A * D < B * C) {
- A = -A;
- B = -B;
- C = -C;
- D = -D;
- XYshear = -XYshear;
- scaleX = -scaleX;
- }
- float rotate = atan2f(B, A);
- aRotate = gfxQuaternion(0, 0, sin(rotate/2), cos(rotate/2));
- aShear[ShearType::XYSHEAR] = XYshear;
- aScale.x = scaleX;
- aScale.y = scaleY;
- aTranslate.x = aMatrix._31;
- aTranslate.y = aMatrix._32;
- return true;
- }
- /**
- * Implementation of the unmatrix algorithm, specified by:
- *
- * http://dev.w3.org/csswg/css3-2d-transforms/#unmatrix
- *
- * This, in turn, refers to the unmatrix program in Graphics Gems,
- * available from http://tog.acm.org/resources/GraphicsGems/ , and in
- * particular as the file GraphicsGems/gemsii/unmatrix.c
- * in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
- */
- bool
- Decompose3DMatrix(const Matrix4x4& aMatrix,
- Point3D& aScale,
- ShearArray& aShear,
- gfxQuaternion& aRotate,
- Point3D& aTranslate,
- Point4D& aPerspective)
- {
- Matrix4x4 local = aMatrix;
- if (local[3][3] == 0) {
- return false;
- }
- /* Normalize the matrix */
- local.Normalize();
- /**
- * perspective is used to solve for perspective, but it also provides
- * an easy way to test for singularity of the upper 3x3 component.
- */
- Matrix4x4 perspective = local;
- Point4D empty(0, 0, 0, 1);
- perspective.SetTransposedVector(3, empty);
- if (perspective.Determinant() == 0.0) {
- return false;
- }
- /* First, isolate perspective. */
- if (local[0][3] != 0 || local[1][3] != 0 ||
- local[2][3] != 0) {
- /* aPerspective is the right hand side of the equation. */
- aPerspective = local.TransposedVector(3);
- /**
- * Solve the equation by inverting perspective and multiplying
- * aPerspective by the inverse.
- */
- perspective.Invert();
- aPerspective = perspective.TransposeTransform4D(aPerspective);
- /* Clear the perspective partition */
- local.SetTransposedVector(3, empty);
- } else {
- aPerspective = Point4D(0, 0, 0, 1);
- }
- /* Next take care of translation */
- for (int i = 0; i < 3; i++) {
- aTranslate[i] = local[3][i];
- local[3][i] = 0;
- }
- /* Now get scale and shear. */
- /* Compute X scale factor and normalize first row. */
- aScale.x = local[0].Length();
- local[0] /= aScale.x;
- /* Compute XY shear factor and make 2nd local orthogonal to 1st. */
- aShear[ShearType::XYSHEAR] = local[0].DotProduct(local[1]);
- local[1] -= local[0] * aShear[ShearType::XYSHEAR];
- /* Now, compute Y scale and normalize 2nd local. */
- aScale.y = local[1].Length();
- local[1] /= aScale.y;
- aShear[ShearType::XYSHEAR] /= aScale.y;
- /* Compute XZ and YZ shears, make 3rd local orthogonal */
- aShear[ShearType::XZSHEAR] = local[0].DotProduct(local[2]);
- local[2] -= local[0] * aShear[ShearType::XZSHEAR];
- aShear[ShearType::YZSHEAR] = local[1].DotProduct(local[2]);
- local[2] -= local[1] * aShear[ShearType::YZSHEAR];
- /* Next, get Z scale and normalize 3rd local. */
- aScale.z = local[2].Length();
- local[2] /= aScale.z;
- aShear[ShearType::XZSHEAR] /= aScale.z;
- aShear[ShearType::YZSHEAR] /= aScale.z;
- /**
- * At this point, the matrix (in locals) is orthonormal.
- * Check for a coordinate system flip. If the determinant
- * is -1, then negate the matrix and the scaling factors.
- */
- if (local[0].DotProduct(local[1].CrossProduct(local[2])) < 0) {
- aScale *= -1;
- for (int i = 0; i < 3; i++) {
- local[i] *= -1;
- }
- }
- /* Now, get the rotations out */
- aRotate = gfxQuaternion(local);
- return true;
- }
- Matrix
- CSSValueArrayTo2DMatrix(nsCSSValue::Array* aArray)
- {
- MOZ_ASSERT(aArray &&
- TransformFunctionOf(aArray) == eCSSKeyword_matrix &&
- aArray->Count() == 7);
- Matrix m(aArray->Item(1).GetFloatValue(),
- aArray->Item(2).GetFloatValue(),
- aArray->Item(3).GetFloatValue(),
- aArray->Item(4).GetFloatValue(),
- aArray->Item(5).GetFloatValue(),
- aArray->Item(6).GetFloatValue());
- return m;
- }
- Matrix4x4
- CSSValueArrayTo3DMatrix(nsCSSValue::Array* aArray)
- {
- MOZ_ASSERT(aArray &&
- TransformFunctionOf(aArray) == eCSSKeyword_matrix3d &&
- aArray->Count() == 17);
- gfx::Float array[16];
- for (size_t i = 0; i < 16; ++i) {
- array[i] = aArray->Item(i+1).GetFloatValue();
- }
- Matrix4x4 m(array);
- return m;
- }
- } // namespace nsStyleTransformMatrix
|