Bug 1429298 - Part 6: Apply motion path transform matrix. r=nical

We implement the layout part of offset-path. Now we don't have
offset-distance, so use the default value, 0%, for it.

Note: rename mCombinedTransform as mIndividualTransform, which only
stores the combined individual transforms. We apply the individual
transforms, motion path transform, and specified transform in
ReadTransforms. (We have to follow the order, so we don't combine the
specified transform in FinishStyle.)

Depends on D2967

Differential Revision: https://phabricator.services.mozilla.com/D2968

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Boris Chiou 2018-08-22 01:22:28 +00:00
Родитель 3563363c59
Коммит bbea8d1bca
10 изменённых файлов: 239 добавлений и 58 удалений

Просмотреть файл

@ -81,6 +81,7 @@ EXPORTS.mozilla.dom += [
'SVGMatrix.h',
'SVGMetadataElement.h',
'SVGMPathElement.h',
'SVGPathData.h',
'SVGPathElement.h',
'SVGPatternElement.h',
'SVGPolygonElement.h',

Просмотреть файл

@ -76,6 +76,7 @@
#include "mozilla/dom/DOMRect.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/dom/KeyframeEffect.h"
#include "mozilla/dom/SVGPathData.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "imgIRequest.h"
#include "nsIImageLoadingContent.h"
@ -10266,3 +10267,94 @@ nsLayoutUtils::StyleForScrollbar(nsIFrame* aScrollbarPart)
// held strongly by the element.
return style.get();
}
static float
ResolveTransformOrigin(const nsStyleCoord& aCoord,
TransformReferenceBox& aRefBox,
TransformReferenceBox::DimensionGetter aGetter)
{
float result = 0.0;
const float scale = mozilla::AppUnitsPerCSSPixel();
if (aCoord.GetUnit() == eStyleUnit_Calc) {
const nsStyleCoord::Calc *calc = aCoord.GetCalcValue();
result = NSAppUnitsToFloatPixels((aRefBox.*aGetter)(), scale) *
calc->mPercent +
NSAppUnitsToFloatPixels(calc->mLength, scale);
} else if (aCoord.GetUnit() == eStyleUnit_Percent) {
result = NSAppUnitsToFloatPixels((aRefBox.*aGetter)(), scale) *
aCoord.GetPercentValue();
} else {
MOZ_ASSERT(aCoord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
result = NSAppUnitsToFloatPixels(aCoord.GetCoordValue(), scale);
}
return result;
}
/* static */ Maybe<MotionPathData>
nsLayoutUtils::ResolveMotionPath(const nsIFrame* aFrame)
{
MOZ_ASSERT(aFrame);
const nsStyleDisplay* display = aFrame->StyleDisplay();
if (!display->mMotion || !display->mMotion->HasPath()) {
return Nothing();
}
const UniquePtr<StyleMotion>& motion = display->mMotion;
// Bug 1429299 - Implement offset-distance for motion path. For now, we use
// the default value, i.e. 0%.
float distance = 0.0;
float angle = 0.0;
Point point;
if (motion->OffsetPath().GetType() == StyleShapeSourceType::Path) {
// Build the path and compute the point and angle for creating the
// equivalent translate and rotate.
// Here we only need to build a valid path for motion path, so
// using the default values of stroke-width, stoke-linecap, and fill-rule
// is fine for now because what we want is get the point and its normal
// vector along the path, instead of rendering it.
// FIXME: Bug 1484780, we should cache the path to avoid rebuilding it here
// at every restyle. (Caching the path avoids the cost of flattening it
// again each time.)
RefPtr<DrawTarget> drawTarget =
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
RefPtr<PathBuilder> builder =
drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
RefPtr<gfx::Path> gfxPath =
SVGPathData::BuildPath(motion->OffsetPath().GetPath()->Path(),
builder,
NS_STYLE_STROKE_LINECAP_BUTT,
0.0);
if (!gfxPath) {
return Nothing();
}
float pathLength = gfxPath->ComputeLength();
float computedDistance = distance * pathLength;
Point tangent;
point = gfxPath->ComputePointAtLength(computedDistance, &tangent);
// Bug 1429301 - Implement offset-rotate for motion path.
// After implement offset-rotate, |angle| will be adjusted more.
// For now, the default value of offset-rotate is "auto", so we use the
// directional tangent vector.
angle = atan2(tangent.y, tangent.x);
} else {
// Bug 1480665: Implement ray() function.
NS_WARNING("Unsupported offset-path value");
}
// Compute the offset for motion path translate.
// We need to resolve transform-origin here to calculate the correct path
// translate. (i.e. Center transform-origin on the path.)
TransformReferenceBox refBox(aFrame);
Point origin(
ResolveTransformOrigin(display->mTransformOrigin[0],
refBox,
&TransformReferenceBox::Width),
ResolveTransformOrigin(display->mTransformOrigin[1],
refBox,
&TransformReferenceBox::Height)
);
// Bug 1186329: the translate parameters will be adjusted more after we
// implement offset-position and offset-anchor.
return Some(MotionPathData { point - origin, angle });
}

Просмотреть файл

@ -119,6 +119,11 @@ struct DisplayPortMarginsPropertyData {
uint32_t mPriority;
};
struct MotionPathData {
gfx::Point mTranslate;
float mRotate;
};
} // namespace mozilla
// For GetDisplayPort
@ -3113,6 +3118,12 @@ public:
*/
static ComputedStyle* StyleForScrollbar(nsIFrame* aScrollbarPart);
/**
* Generate the motion path transform result.
**/
static mozilla::Maybe<mozilla::MotionPathData>
ResolveMotionPath(const nsIFrame* aFrame);
private:
static uint32_t sFontSizeInflationEmPerLine;
static uint32_t sFontSizeInflationMinTwips;

Просмотреть файл

@ -249,19 +249,27 @@ static void
IncrementScaleRestyleCountIfNeeded(nsIFrame* aFrame, LayerActivity* aActivity)
{
const nsStyleDisplay* display = aFrame->StyleDisplay();
RefPtr<nsCSSValueSharedList> transformList = display->GetCombinedTransform();
if (!transformList) {
if (!display->mSpecifiedTransform &&
!display->HasIndividualTransform() &&
!(display->mMotion && display->mMotion->HasPath())) {
// The transform was removed.
aActivity->mPreviousTransformScale = Nothing();
IncrementMutationCount(&aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]);
IncrementMutationCount(
&aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]);
return;
}
// Compute the new scale due to the CSS transform property.
bool dummyBool;
nsStyleTransformMatrix::TransformReferenceBox refBox(aFrame);
Matrix4x4 transform =
nsStyleTransformMatrix::ReadTransforms(transformList->mHead,
Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms(
display->mIndividualTransform
? display->mIndividualTransform->mHead
: nullptr,
nsLayoutUtils::ResolveMotionPath(aFrame),
display->mSpecifiedTransform
? display->mSpecifiedTransform->mHead
: nullptr,
refBox,
AppUnitsPerCSSPixel(),
&dummyBool);

Просмотреть файл

@ -7849,11 +7849,14 @@ nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
return true;
}
nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsIFrame* aFrame,
nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
const nsIFrame* aFrame,
float aAppUnitsPerPixel,
const nsRect* aBoundsOverride)
: mFrame(aFrame)
, mTransformList(aFrame->StyleDisplay()->GetCombinedTransform())
, mIndividualTransformList(aFrame->StyleDisplay()->mIndividualTransform)
, mMotion(nsLayoutUtils::ResolveMotionPath(aFrame))
, mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform)
, mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride))
{
}
@ -7922,10 +7925,16 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
frame && frame->IsSVGTransformed(&svgTransform,
&parentsChildrenOnlyTransform);
/* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */
if (aProperties.mTransformList) {
result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead,
refBox, aAppUnitsPerPixel,
&dummyBool);
if (aProperties.HasTransform()) {
result = nsStyleTransformMatrix::ReadTransforms(
aProperties.mIndividualTransformList
? aProperties.mIndividualTransformList->mHead
: nullptr,
aProperties.mMotion,
aProperties.mTransformList
? aProperties.mTransformList->mHead
: nullptr,
refBox, aAppUnitsPerPixel, &dummyBool);
} else if (hasSVGTransforms) {
// Correct the translation components for zoom:
float pixelsPerCSSPx = AppUnitsPerCSSPixel() /

Просмотреть файл

@ -68,6 +68,7 @@ enum class nsDisplayOwnLayerFlags;
namespace mozilla {
class FrameLayerBuilder;
struct MotionPathData;
namespace layers {
class Layer;
class ImageLayer;
@ -6577,6 +6578,11 @@ public:
FrameTransformProperties(const nsIFrame* aFrame,
float aAppUnitsPerPixel,
const nsRect* aBoundsOverride);
// This constructor is used on the compositor (for animations).
// Bug 1186329, Bug 1425837, If we want to support compositor animationsf
// or individual transforms and motion path, we may need to update this.
// For now, let mIndividualTransformList and mMotion as nullptr and
// Nothing().
FrameTransformProperties(RefPtr<const nsCSSValueSharedList>&&
aTransformList,
const Point3D& aToTransformOrigin)
@ -6585,7 +6591,14 @@ public:
, mToTransformOrigin(aToTransformOrigin)
{}
bool HasTransform() const
{
return mIndividualTransformList || mTransformList || mMotion.isSome();
}
const nsIFrame* mFrame;
const RefPtr<const nsCSSValueSharedList> mIndividualTransformList;
const mozilla::Maybe<mozilla::MotionPathData> mMotion;
const RefPtr<const nsCSSValueSharedList> mTransformList;
const Point3D mToTransformOrigin;
};

Просмотреть файл

@ -3636,10 +3636,10 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource)
, mSpecifiedRotate(aSource.mSpecifiedRotate)
, mSpecifiedTranslate(aSource.mSpecifiedTranslate)
, mSpecifiedScale(aSource.mSpecifiedScale)
, mIndividualTransform(aSource.mIndividualTransform)
, mMotion(aSource.mMotion
? MakeUnique<StyleMotion>(*aSource.mMotion)
: nullptr)
, mCombinedTransform(aSource.mCombinedTransform)
, mTransformOrigin{ aSource.mTransformOrigin[0],
aSource.mTransformOrigin[1],
aSource.mTransformOrigin[2] }
@ -3704,9 +3704,8 @@ nsStyleDisplay::~nsStyleDisplay()
mSpecifiedTranslate);
ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedScale",
mSpecifiedScale);
ReleaseSharedListOnMainThread("nsStyleDisplay::mCombinedTransform",
mCombinedTransform);
ReleaseSharedListOnMainThread("nsStyleDisplay::mIndividualTransform",
mIndividualTransform);
MOZ_COUNT_DTOR(nsStyleDisplay);
}
@ -3734,7 +3733,7 @@ nsStyleDisplay::FinishStyle(
}
}
GenerateCombinedTransform();
GenerateCombinedIndividualTransform();
}
static inline nsChangeHint
@ -4031,17 +4030,17 @@ nsStyleDisplay::CalcDifference(const nsStyleDisplay& aNewData) const
}
void
nsStyleDisplay::GenerateCombinedTransform()
nsStyleDisplay::GenerateCombinedIndividualTransform()
{
// FIXME(emilio): This should probably be called from somewhere like what we
// do for image layers, instead of FinishStyle.
//
// This does and undoes the work a ton of times in Stylo.
mCombinedTransform = nullptr;
mIndividualTransform = nullptr;
// Follow the order defined in the spec to append transform functions.
// https://drafts.csswg.org/css-transforms-2/#ctm
AutoTArray<nsCSSValueSharedList*, 4> shareLists;
AutoTArray<nsCSSValueSharedList*, 3> shareLists;
if (mSpecifiedTranslate) {
shareLists.AppendElement(mSpecifiedTranslate.get());
}
@ -4051,24 +4050,20 @@ nsStyleDisplay::GenerateCombinedTransform()
if (mSpecifiedScale) {
shareLists.AppendElement(mSpecifiedScale.get());
}
if (mSpecifiedTransform) {
shareLists.AppendElement(mSpecifiedTransform.get());
}
if (shareLists.Length() == 0) {
return;
}
if (shareLists.Length() == 1) {
mCombinedTransform = shareLists[0];
mIndividualTransform = shareLists[0];
return;
}
// In common, we may have 3 transform functions(for rotate, translate and
// scale) in mSpecifiedTransform, one rotate function in mSpecifiedRotate,
// one translate function in mSpecifiedTranslate, and one scale function in
// mSpecifiedScale. So 6 slots are enough for the most cases.
AutoTArray<nsCSSValueList*, 6> valueLists;
// In common, we may have 3 transform functions:
// 1. one rotate function in mSpecifiedRotate,
// 2. one translate function in mSpecifiedTranslate,
// 3. one scale function in mSpecifiedScale.
AutoTArray<nsCSSValueList*, 3> valueLists;
for (auto list: shareLists) {
if (list) {
valueLists.AppendElement(list->mHead->Clone());
@ -4083,8 +4078,9 @@ nsStyleDisplay::GenerateCombinedTransform()
valueLists[i]->mNext = valueLists[i + 1];
}
mCombinedTransform = new nsCSSValueSharedList(valueLists[0]);
mIndividualTransform = new nsCSSValueSharedList(valueLists[0]);
}
// --------------------
// nsStyleVisibility
//

Просмотреть файл

@ -2181,14 +2181,11 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay
RefPtr<nsCSSValueSharedList> mSpecifiedRotate;
RefPtr<nsCSSValueSharedList> mSpecifiedTranslate;
RefPtr<nsCSSValueSharedList> mSpecifiedScale;
// Used to store the final combination of mSpecifiedRotate,
// mSpecifiedTranslate, and mSpecifiedScale.
RefPtr<nsCSSValueSharedList> mIndividualTransform;
mozilla::UniquePtr<mozilla::StyleMotion> mMotion;
// Used to store the final combination of mSpecifiedTranslate,
// mSpecifiedRotate, mSpecifiedScale and mSpecifiedTransform.
// Use GetCombinedTransform() to get the final transform, instead of
// accessing mCombinedTransform directly.
RefPtr<nsCSSValueSharedList> mCombinedTransform;
nsStyleCoord mTransformOrigin[3]; // percent, coord, calc, 3rd param is coord, calc only
nsStyleCoord mChildPerspective; // none, coord
nsStyleCoord mPerspectiveOrigin[2]; // percent, coord, calc
@ -2528,21 +2525,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay
inline bool IsFixedPosContainingBlockForTransformSupportingFrames() const;
/**
* Returns the final combined transform.
* Returns the final combined individual transform.
**/
already_AddRefed<nsCSSValueSharedList> GetCombinedTransform() const {
if (mCombinedTransform) {
return do_AddRef(mCombinedTransform);
}
// backward compatible to gecko-backed style system.
return mSpecifiedTransform ? do_AddRef(mSpecifiedTransform) : nullptr;
already_AddRefed<nsCSSValueSharedList> GetCombinedTransform() const
{
return mIndividualTransform ? do_AddRef(mIndividualTransform) : nullptr;
}
private:
// Helpers for above functions, which do some but not all of the tests
// for them (since transform must be tested separately for each).
void GenerateCombinedTransform();
void GenerateCombinedIndividualTransform();
};
struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTable

Просмотреть файл

@ -926,14 +926,12 @@ SetIdentityMatrix(nsCSSValue::Array* aMatrix)
}
}
Matrix4x4
ReadTransforms(const nsCSSValueList* aList,
static void
ReadTransformsImpl(Matrix4x4& aMatrix,
const nsCSSValueList* aList,
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) {
@ -947,9 +945,55 @@ ReadTransforms(const nsCSSValueList* aList,
"Incoming function is too short!");
/* Read in a single transform matrix. */
MatrixForTransformFunction(result, currElem.GetArrayValue(), aRefBox,
MatrixForTransformFunction(aMatrix, currElem.GetArrayValue(), aRefBox,
aContains3dTransform);
}
}
Matrix4x4
ReadTransforms(const nsCSSValueList* aList,
TransformReferenceBox& aRefBox,
float aAppUnitsPerMatrixUnit,
bool* aContains3dTransform)
{
Matrix4x4 result;
ReadTransformsImpl(result, aList, aRefBox, aContains3dTransform);
float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;
result.PreScale(1/scale, 1/scale, 1/scale);
result.PostScale(scale, scale, scale);
return result;
}
Matrix4x4
ReadTransforms(const nsCSSValueList* aIndividualTransforms,
const Maybe<MotionPathData>& aMotion,
const nsCSSValueList* aTransform,
TransformReferenceBox& aRefBox,
float aAppUnitsPerMatrixUnit,
bool* aContains3dTransform)
{
Matrix4x4 result;
if (aIndividualTransforms) {
ReadTransformsImpl(result, aIndividualTransforms, aRefBox,
aContains3dTransform);
}
if (aMotion.isSome()) {
// Create the equivalent translate and rotate function, according to the
// order in spec. We combine the translate and then the rotate.
// https://drafts.fxtf.org/motion-1/#calculating-path-transform
result.PreTranslate(aMotion->mTranslate.x, aMotion->mTranslate.y, 0.0);
if (aMotion->mRotate != 0.0) {
result.RotateZ(aMotion->mRotate);
}
}
if (aTransform) {
ReadTransformsImpl(result, aTransform, aRefBox, aContains3dTransform);
}
float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;
result.PreScale(1/scale, 1/scale, 1/scale);

Просмотреть файл

@ -24,6 +24,10 @@ class nsPresContext;
struct gfxQuaternion;
struct nsRect;
namespace mozilla {
struct MotionPathData;
}
/**
* A helper to generate gfxMatrixes from css transform functions.
*/
@ -200,6 +204,16 @@ namespace nsStyleTransformMatrix {
float aAppUnitsPerMatrixUnit,
bool* aContains3dTransform);
// Generate the gfx::Matrix for CSS Transform Module Level 2.
// https://drafts.csswg.org/css-transforms-2/#ctm
mozilla::gfx::Matrix4x4
ReadTransforms(const nsCSSValueList* aIndividualTransforms,
const mozilla::Maybe<mozilla::MotionPathData>& aMotion,
const nsCSSValueList* aTransform,
TransformReferenceBox& aRefBox,
float aAppUnitsPerMatrixUnit,
bool* aContains3dTransform);
/**
* Given two nsStyleCoord values, compute the 2d position with respect to the
* given TransformReferenceBox that these values describe, in device pixels.