Bug 1168263 - Add nsDisplayPerspective and build separate layers for perspective. r=roc

This commit is contained in:
Matt Woodrow 2015-11-26 22:32:36 +13:00
parent c66ea2a1c2
commit a957c40d8f
9 changed files with 334 additions and 101 deletions

View File

@ -867,10 +867,10 @@ Factory::CreateWrappingDataSourceSurface(uint8_t *aData, int32_t aStride,
const IntSize &aSize,
SurfaceFormat aFormat)
{
MOZ_ASSERT(aData);
if (aSize.width <= 0 || aSize.height <= 0) {
return nullptr;
}
MOZ_ASSERT(aData);
RefPtr<SourceSurfaceRawData> newSurf = new SourceSurfaceRawData();

View File

@ -504,7 +504,7 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
static void
SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
StyleAnimationValue& aEnd, Animatable* aValue)
StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
{
StyleAnimationValue interpolatedValue;
NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
@ -525,16 +525,21 @@ SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
nsPoint origin = data.origin();
// we expect all our transform data to arrive in device pixels
Point3D transformOrigin = data.transformOrigin();
Point3D perspectiveOrigin = data.perspectiveOrigin();
nsDisplayTransform::FrameTransformProperties props(interpolatedList,
transformOrigin,
perspectiveOrigin,
data.perspective());
transformOrigin);
// If our parent layer is a perspective layer, then the offset into reference
// frame coordinates is already on that layer. If not, then we need to ask
// for it to be added here.
uint32_t flags = 0;
if (!aLayer->GetParent() || !aLayer->GetParent()->GetTransformIsPerspective()) {
flags = nsDisplayTransform::OFFSET_BY_ORIGIN;
}
Matrix4x4 transform =
nsDisplayTransform::GetResultingTransformMatrix(props, origin,
data.appUnitsPerDevPixel(),
nsDisplayTransform::OFFSET_BY_ORIGIN,
&data.bounds());
flags, &data.bounds());
InfallibleTArray<TransformFunction> functions;
functions.AppendElement(TransformMatrix(transform));
@ -612,7 +617,7 @@ SampleAnimations(Layer* aLayer, TimeStamp aPoint)
// interpolate the property
Animatable interpolatedValue;
SampleValue(portion, animation, animData.mStartValues[segmentIndex],
animData.mEndValues[segmentIndex], &interpolatedValue);
animData.mEndValues[segmentIndex], &interpolatedValue, aLayer);
LayerComposite* layerComposite = aLayer->AsLayerComposite();
switch (animation.property()) {
case eCSSProperty_opacity:

View File

@ -744,7 +744,12 @@ LayerTransactionParent::RecvGetAnimationTransform(PLayerParent* aParent,
}
}
transform.PostTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
// If our parent isn't a perspective layer, then the offset into reference
// frame coordinates will have been applied to us. Add an inverse translation
// to cancel it out.
if (!layer->GetParent() || !layer->GetParent()->GetTransformIsPerspective()) {
transform.PostTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
}
// Undo the rebasing applied by
// nsDisplayTransform::GetResultingTransformMatrixInternal

View File

@ -164,10 +164,7 @@ struct TransformData {
nsPoint origin;
// the transform-origin property for the transform in device pixels
Point3D transformOrigin;
// the perspective-origin property for the transform in device pixels
Point3D perspectiveOrigin;
nsRect bounds;
nscoord perspective;
int32_t appUnitsPerDevPixel;
};

View File

@ -675,6 +675,7 @@ struct NewLayerEntry {
, mPropagateComponentAlphaFlattening(true)
, mUntransformedVisibleRegion(false)
, mIsCaret(false)
, mIsPerspectiveItem(false)
{}
// mLayer is null if the previous entry is for a PaintedLayer that hasn't
// been optimized to some other form (yet).
@ -716,6 +717,7 @@ struct NewLayerEntry {
// transform.
bool mUntransformedVisibleRegion;
bool mIsCaret;
bool mIsPerspectiveItem;
};
class PaintedLayerDataTree;
@ -4116,6 +4118,9 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
newLayerEntry->mAnimatedGeometryRoot = animatedGeometryRoot;
newLayerEntry->mAnimatedGeometryRootForScrollMetadata = animatedGeometryRootForScrollMetadata;
newLayerEntry->mFixedPosFrameForLayerData = fixedPosFrame;
if (itemType == nsDisplayItem::TYPE_PERSPECTIVE) {
newLayerEntry->mIsPerspectiveItem = true;
}
// Don't attempt to flatten compnent alpha layers that are within
// a forced active layer, or an active transform;
@ -4132,9 +4137,11 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
"Transform items must set layerContentsVisibleRect!");
if (mLayerBuilder->IsBuildingRetainedLayers()) {
newLayerEntry->mLayerContentsVisibleRect = layerContentsVisibleRect;
if (itemType == nsDisplayItem::TYPE_TRANSFORM &&
(item->Frame()->Extend3DContext() ||
item->Frame()->Combines3DTransformWithAncestors())) {
if (itemType == nsDisplayItem::TYPE_PERSPECTIVE ||
(itemType == nsDisplayItem::TYPE_TRANSFORM &&
(item->Frame()->Extend3DContext() ||
item->Frame()->Combines3DTransformWithAncestors() ||
item->Frame()->HasPerspective()))) {
// Give untransformed visible region as outer visible region
// to avoid failure caused by singular transforms.
newLayerEntry->mUntransformedVisibleRegion = true;
@ -5093,7 +5100,9 @@ ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder,
bool canDraw2D = transform.CanDraw2D(&transform2d);
gfxSize scale;
// XXX Should we do something for 3D transforms?
if (canDraw2D && !aContainerFrame->Combines3DTransformWithAncestors()) {
if (canDraw2D &&
!aContainerFrame->Combines3DTransformWithAncestors() &&
!aContainerFrame->HasPerspective()) {
// If the container's transform is animated off main thread, fix a suitable scale size
// for animation
if (aContainerItem &&

View File

@ -64,6 +64,7 @@ DECLARE_DISPLAY_ITEM_TYPE(TEXT_DECORATION)
DECLARE_DISPLAY_ITEM_TYPE(TEXT_OVERFLOW)
DECLARE_DISPLAY_ITEM_TYPE(TEXT_SHADOW)
DECLARE_DISPLAY_ITEM_TYPE_FLAGS(TRANSFORM,TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE_FLAGS(PERSPECTIVE,TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE(VIDEO)
DECLARE_DISPLAY_ITEM_TYPE(WRAP_LIST)
DECLARE_DISPLAY_ITEM_TYPE(ZOOM)

View File

@ -567,16 +567,6 @@ nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
float scale = devPixelsToAppUnits;
Point3D offsetToTransformOrigin =
nsDisplayTransform::GetDeltaToTransformOrigin(aFrame, scale, &bounds);
Point3D offsetToPerspectiveOrigin =
nsDisplayTransform::GetDeltaToPerspectiveOrigin(aFrame, scale);
nscoord perspective = 0.0;
nsStyleContext* parentStyleContext = aFrame->StyleContext()->GetParent();
if (parentStyleContext) {
const nsStyleDisplay* disp = parentStyleContext->StyleDisplay();
if (disp && disp->mChildPerspective.GetUnit() == eStyleUnit_Coord) {
perspective = disp->mChildPerspective.GetCoordValue();
}
}
nsPoint origin;
if (aItem) {
// This branch is for display items to leverage the cache of
@ -594,8 +584,7 @@ nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
}
data = TransformData(origin, offsetToTransformOrigin,
offsetToPerspectiveOrigin, bounds, perspective,
devPixelsToAppUnits);
bounds, devPixelsToAppUnits);
} else if (aProperty == eCSSProperty_opacity) {
data = null_t();
}
@ -631,6 +620,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarFlags(0),
mPerspectiveItemIndex(0),
mIsBuildingScrollbar(false),
mCurrentScrollbarWillHaveLayer(false),
mBuildCaret(aBuildCaret),
@ -5072,51 +5062,61 @@ nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
aAppUnitsPerPixel));
}
/* Returns the delta specified by the -moz-perspective-origin property.
* This is a positive delta, meaning that it indicates the direction to move
* to get from (0, 0) of the frame to the perspective origin. This function is
* called off the main thread.
*/
/* static */ Point3D
nsDisplayTransform::GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
float aAppUnitsPerPixel)
/* static */ bool
nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
float aAppUnitsPerPixel,
Matrix4x4& aOutMatrix)
{
NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
NS_PRECONDITION(aFrame->IsTransformed() ||
aFrame->StyleDisplay()->BackfaceIsHidden() ||
aFrame->Combines3DTransformWithAncestors(),
"Shouldn't get a delta for an untransformed frame!");
NS_PRECONDITION(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
if (!aFrame->IsTransformed()) {
return Point3D();
return false;
}
/* For both of the coordinates, if the value of -moz-perspective-origin is a
* percentage, it's relative to the size of the frame. Otherwise, if it's
* a distance, it's already computed for us!
/* Find our containing block, which is the element that provides the
* value for perspective we need to use
*/
//TODO: Should this be using our bounds or the parent's bounds?
// How do we handle aBoundsOverride in the latter case?
//TODO: Is it possible that the cbFrame's bounds haven't been set correctly yet
// (similar to the aBoundsOverride case for GetResultingTransformMatrix)?
nsIFrame* cbFrame = aFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME);
if (!cbFrame) {
return Point3D();
return false;
}
const nsStyleDisplay* display = cbFrame->StyleDisplay();
/* Grab the values for perspective and perspective-origin (if present) */
const nsStyleDisplay* cbDisplay = cbFrame->StyleDisplay();
if (cbDisplay->mChildPerspective.GetUnit() != eStyleUnit_Coord) {
return false;
}
nscoord perspective = cbDisplay->mChildPerspective.GetCoordValue();
if (perspective <= 0) {
return false;
}
TransformReferenceBox refBox(cbFrame);
/* Allows us to access named variables by index. */
Point3D result;
result.z = 0.0f;
gfx::Float* coords[2] = {&result.x, &result.y};
Point3D perspectiveOrigin;
gfx::Float* coords[2] = {&perspectiveOrigin.x, &perspectiveOrigin.y};
TransformReferenceBox::DimensionGetter dimensionGetter[] =
{ &TransformReferenceBox::Width, &TransformReferenceBox::Height };
/* For both of the coordinates, if the value of perspective-origin is a
* percentage, it's relative to the size of the frame. Otherwise, if it's
* a distance, it's already computed for us!
*/
for (uint8_t index = 0; index < 2; ++index) {
/* If the transform-origin specifies a percentage, take the percentage
/* If the -transform-origin specifies a percentage, take the percentage
* of the size of the box.
*/
const nsStyleCoord &coord = display->mPerspectiveOrigin[index];
const nsStyleCoord &coord = cbDisplay->mPerspectiveOrigin[index];
if (coord.GetUnit() == eStyleUnit_Calc) {
const nsStyleCoord::Calc *calc = coord.GetCalcValue();
*coords[index] =
@ -5144,7 +5144,15 @@ nsDisplayTransform::GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
NSAppUnitsToFloatPixels(frameToCbOffset.y, aAppUnitsPerPixel),
0.0f);
return result + frameToCbGfxOffset;
/* Move the perspective origin to be relative to aFrame, instead of relative
* to the containing block which is how it was specified in the style system.
*/
perspectiveOrigin += frameToCbGfxOffset;
aOutMatrix._34 =
-1.0 / NSAppUnitsToFloatPixels(perspective, aAppUnitsPerPixel);
aOutMatrix.ChangeBasis(perspectiveOrigin);
return true;
}
nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsIFrame* aFrame,
@ -5153,20 +5161,7 @@ nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsI
: mFrame(aFrame)
, mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform)
, mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride))
, mChildPerspective(0)
{
nsIFrame* cbFrame = aFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME);
if (cbFrame) {
const nsStyleDisplay* display = cbFrame->StyleDisplay();
if (display->mChildPerspective.GetUnit() == eStyleUnit_Coord) {
mChildPerspective = display->mChildPerspective.GetCoordValue();
// Calling GetDeltaToPerspectiveOrigin can be expensive, so we avoid
// calling it unnecessarily.
if (mChildPerspective > 0.0) {
mToPerspectiveOrigin = GetDeltaToPerspectiveOrigin(aFrame, aAppUnitsPerPixel);
}
}
}
}
/* Wraps up the transform matrix in a change-of-basis matrix pair that
@ -5210,6 +5205,7 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
nsIFrame** aOutAncestor)
{
const nsIFrame *frame = aProperties.mFrame;
NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE), "Must have a frame to compute perspective!");
if (aOutAncestor) {
*aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(frame);
@ -5264,7 +5260,12 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y),
0);
bool hasPerspective = aProperties.mChildPerspective > 0.0;
Matrix4x4 perspectiveMatrix;
bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
if (hasPerspective) {
hasPerspective = ComputePerspectiveMatrix(frame, aAppUnitsPerPixel,
perspectiveMatrix);
}
if (!hasSVGTransforms || !hasTransformFromSVGParent) {
// This is a simplification of the following |else| block, the
@ -5314,12 +5315,7 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
}
if (hasPerspective) {
Matrix4x4 perspective;
perspective._34 =
-1.0 / NSAppUnitsToFloatPixels(aProperties.mChildPerspective, aAppUnitsPerPixel);
perspective.ChangeBasis(aProperties.GetToPerspectiveOrigin());
result = result * perspective;
result = result * perspectiveMatrix;
if (aFlags & OFFSET_BY_ORIGIN) {
result.PostTranslate(roundedOrigin);
@ -5341,7 +5337,7 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
// then we're not a reference frame so no offset to origin will be added. Our
// parent transform however *is* the reference frame, so we pass
// OFFSET_BY_ORIGIN to convert into the correct coordinate space.
uint32_t flags = aFlags & (INCLUDE_PRESERVE3D_ANCESTORS);
uint32_t flags = aFlags & (INCLUDE_PRESERVE3D_ANCESTORS|INCLUDE_PERSPECTIVE);
if (!frame->IsTransformed()) {
flags |= OFFSET_BY_ORIGIN;
}
@ -5493,7 +5489,7 @@ nsDisplayTransform::GetTransform()
bool isReference =
mFrame->IsTransformed() ||
mFrame->Combines3DTransformWithAncestors() || mFrame->Extend3DContext();
uint32_t flags = 0;
uint32_t flags = INCLUDE_PERSPECTIVE;
if (isReference) {
flags |= OFFSET_BY_ORIGIN;
}
@ -5504,6 +5500,20 @@ nsDisplayTransform::GetTransform()
return mTransform;
}
Matrix4x4
nsDisplayTransform::GetTransformForRendering()
{
if (!mFrame->HasPerspective() || mTransformGetter || mIsTransformSeparator) {
return GetTransform();
}
MOZ_ASSERT(!mTransformGetter);
float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
// Don't include perspective transform, or the offset to origin, since
// nsDisplayPerspective will handle both of those.
return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
}
const Matrix4x4&
nsDisplayTransform::GetAccumulatedPreserved3DTransform(nsDisplayListBuilder* aBuilder)
{
@ -5527,7 +5537,7 @@ nsDisplayTransform::GetAccumulatedPreserved3DTransform(nsDisplayListBuilder* aBu
nsPoint offset = mFrame->GetOffsetToCrossDoc(establisherReference);
float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
uint32_t flags = INCLUDE_PRESERVE3D_ANCESTORS|OFFSET_BY_ORIGIN;
uint32_t flags = INCLUDE_PRESERVE3D_ANCESTORS|INCLUDE_PERSPECTIVE|OFFSET_BY_ORIGIN;
mTransformPreserves3D =
GetResultingTransformMatrix(mFrame, offset, scale, flags);
}
@ -5551,7 +5561,7 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu
* backface hidden here. But, it would be removed by the init
* function of nsDisplayTransform.
*/
const Matrix4x4& newTransformMatrix = GetTransform();
const Matrix4x4& newTransformMatrix = GetTransformForRendering();
uint32_t flags = ShouldPrerender(aBuilder) ?
FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0;
@ -5956,7 +5966,7 @@ nsRect nsDisplayTransform::TransformRect(const nsRect &aUntransformedBounds,
float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
uint32_t flags = 0;
uint32_t flags = INCLUDE_PERSPECTIVE;
if (aPreserves3D) {
flags |= INCLUDE_PRESERVE3D_ANCESTORS;
}
@ -5977,7 +5987,7 @@ bool nsDisplayTransform::UntransformRect(const nsRect &aTransformedBounds,
float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
uint32_t flags = 0;
uint32_t flags = INCLUDE_PERSPECTIVE;
if (aPreserves3D) {
flags |= INCLUDE_PRESERVE3D_ANCESTORS;
}
@ -6037,6 +6047,71 @@ nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream)
AppendToString(aStream, GetTransform());
}
nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
nsIFrame* aTransformFrame,
nsIFrame* aPerspectiveFrame,
nsDisplayList* aList)
: nsDisplayItem(aBuilder, aPerspectiveFrame)
, mList(aBuilder, aPerspectiveFrame, aList)
, mTransformFrame(aTransformFrame)
, mIndex(aBuilder->AllocatePerspectiveItemIndex())
{}
already_AddRefed<Layer>
nsDisplayPerspective::BuildLayer(nsDisplayListBuilder *aBuilder,
LayerManager *aManager,
const ContainerLayerParameters& aContainerParameters)
{
float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
Matrix4x4 perspectiveMatrix;
DebugOnly<bool> hasPerspective =
nsDisplayTransform::ComputePerspectiveMatrix(mTransformFrame, appUnitsPerPixel,
perspectiveMatrix);
MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
/*
* The resulting matrix is still in the coordinate space of the transformed
* frame. Append a translation to the reference frame coordinates.
*/
nsDisplayTransform* transform =
static_cast<nsDisplayTransform*>(mList.GetChildren()->GetTop());
Point3D newOrigin =
Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x, appUnitsPerPixel),
NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y, appUnitsPerPixel),
0.0f);
Point3D roundedOrigin(NS_round(newOrigin.x),
NS_round(newOrigin.y),
0);
perspectiveMatrix.PostTranslate(roundedOrigin);
RefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList.GetChildren(),
aContainerParameters, &perspectiveMatrix, 0);
if (!container) {
return nullptr;
}
// Sort of a lie, but we want to pretend that the perspective layer extends a 3d context
// so that it gets its transform combined with children. Might need a better name that reflects
// this use case and isn't specific to preserve-3d.
container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_EXTEND_3D_CONTEXT);
container->SetTransformIsPerspective(true);
return container.forget();
}
LayerState
nsDisplayPerspective::GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters)
{
return LAYER_ACTIVE;
}
nsDisplayItemGeometry*
nsCharClipDisplayItem::AllocateGeometry(nsDisplayListBuilder* aBuilder)
{

View File

@ -743,6 +743,32 @@ public:
bool mOldValue;
};
class AutoSaveRestorePerspectiveIndex;
friend class AutoSaveRestorePerspectiveIndex;
class AutoSaveRestorePerspectiveIndex {
public:
AutoSaveRestorePerspectiveIndex(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
: mBuilder(nullptr)
{
if (aFrame->ChildrenHavePerspective()) {
mBuilder = aBuilder;
mCachedItemIndex = aBuilder->mPerspectiveItemIndex;
aBuilder->mPerspectiveItemIndex = 0;
}
}
~AutoSaveRestorePerspectiveIndex()
{
if (mBuilder) {
mBuilder->mPerspectiveItemIndex = mCachedItemIndex;
}
}
private:
nsDisplayListBuilder* mBuilder;
uint32_t mCachedItemIndex;
};
/**
* A helper class to temporarily set the value of mCurrentScrollParentId.
*/
@ -995,6 +1021,8 @@ public:
return mContainedBlendModes;
}
uint32_t AllocatePerspectiveItemIndex() { return mPerspectiveItemIndex++; }
DisplayListClipState& ClipState() { return mClipState; }
/**
@ -1199,6 +1227,7 @@ private:
uint32_t mCurrentScrollbarFlags;
BlendModeSet mContainedBlendModes;
Preserves3DContext mPreserves3DCtx;
uint32_t mPerspectiveItemIndex;
bool mIsBuildingScrollbar;
bool mCurrentScrollbarWillHaveLayer;
bool mBuildCaret;
@ -3815,7 +3844,15 @@ public:
INDEX_MAX = UINT32_MAX >> nsDisplayItem::TYPE_BITS
};
/**
* We include the perspective matrix from our containing block for the
* purposes of visibility calculations, but we exclude it from the transform
* we set on the layer (for rendering), since there will be an
* nsDisplayPerspective created for that.
*/
const Matrix4x4& GetTransform();
Matrix4x4 GetTransformForRendering();
/**
* Return the transform that is aggregation of all transform on the
* preserves3d chain.
@ -3865,8 +3902,18 @@ public:
float aAppUnitsPerPixel,
const nsRect* aBoundsOverride);
static Point3D GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
float aAppUnitsPerPixel);
/*
* Returns true if aFrame has perspective applied from its containing
* block.
* Returns the matrix to append to apply the persective (taking
* perspective-origin into account), relative to aFrames coordinate
* space).
* aOutMatrix is assumed to be the identity matrix, and isn't explicitly
* cleared.
*/
static bool ComputePerspectiveMatrix(const nsIFrame* aFrame,
float aAppUnitsPerPixel,
Matrix4x4& aOutMatrix);
struct FrameTransformProperties
{
@ -3874,30 +3921,15 @@ public:
float aAppUnitsPerPixel,
const nsRect* aBoundsOverride);
FrameTransformProperties(nsCSSValueSharedList* aTransformList,
const Point3D& aToTransformOrigin,
const Point3D& aToPerspectiveOrigin,
nscoord aChildPerspective)
const Point3D& aToTransformOrigin)
: mFrame(nullptr)
, mTransformList(aTransformList)
, mToTransformOrigin(aToTransformOrigin)
, mChildPerspective(aChildPerspective)
, mToPerspectiveOrigin(aToPerspectiveOrigin)
{}
const Point3D& GetToPerspectiveOrigin() const
{
MOZ_ASSERT(mChildPerspective > 0, "Only valid with mChildPerspective > 0");
return mToPerspectiveOrigin;
}
const nsIFrame* mFrame;
RefPtr<nsCSSValueSharedList> mTransformList;
const Point3D mToTransformOrigin;
nscoord mChildPerspective;
private:
// mToPerspectiveOrigin is only valid if mChildPerspective > 0.
Point3D mToPerspectiveOrigin;
};
/**
@ -3917,10 +3949,13 @@ public:
* @param aFlags INCLUDE_PRESERVE3D_ANCESTORS The computed transform will
* include the transform of any ancestors participating in the same
* 3d rendering context.
* @param aFlags INCLUDE_PERSPECTIVE The resulting matrix will include the
* perspective transform from the containing block if applicable.
*/
enum {
OFFSET_BY_ORIGIN = 1 << 0,
INCLUDE_PRESERVE3D_ANCESTORS = 1 << 1,
INCLUDE_PERSPECTIVE = 1 << 2,
};
static Matrix4x4 GetResultingTransformMatrix(const nsIFrame* aFrame,
const nsPoint& aOrigin,
@ -4046,6 +4081,85 @@ private:
bool mTransformPreserves3DInited;
};
/* A display item that applies a perspective transformation to a single
* nsDisplayTransform child item. We keep this as a separate item since the
* perspective-origin is relative to an ancestor of the transformed frame, and
* APZ can scroll the child separately.
*/
class nsDisplayPerspective : public nsDisplayItem
{
typedef mozilla::gfx::Point3D Point3D;
public:
NS_DISPLAY_DECL_NAME("nsDisplayPerspective", TYPE_PERSPECTIVE)
nsDisplayPerspective(nsDisplayListBuilder* aBuilder, nsIFrame* aTransformFrame,
nsIFrame* aPerspectiveFrame,
nsDisplayList* aList);
virtual uint32_t GetPerFrameKey() override { return (mIndex << nsDisplayItem::TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); }
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override
{
return mList.HitTest(aBuilder, aRect, aState, aOutFrames);
}
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
{
return mList.GetBounds(aBuilder, aSnap);
}
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion* aInvalidRegion) override
{}
virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
bool* aSnap) override
{
return mList.GetOpaqueRegion(aBuilder, aSnap);
}
virtual bool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) override
{
return mList.IsUniform(aBuilder, aColor);
}
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters) override;
virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) override
{
return mList.ShouldBuildLayerEvenIfInvisible(aBuilder);
}
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters) override;
virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion) override
{
mList.RecomputeVisibility(aBuilder, aVisibleRegion);
return true;
}
virtual nsDisplayList* GetSameCoordinateSystemChildren() override { return mList.GetChildren(); }
virtual nsDisplayList* GetChildren() override { return mList.GetChildren(); }
virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
{
return mList.GetComponentAlphaBounds(aBuilder);
}
nsIFrame* TransformFrame() { return mTransformFrame; }
private:
nsDisplayWrapList mList;
nsIFrame* mTransformFrame;
uint32_t mIndex;
};
/**
* This class adds basic support for limiting the rendering (in the inline axis
* of the writing mode) to the part inside the specified edges. It's a base

View File

@ -1895,7 +1895,7 @@ CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
* True if aDescendant participates the context aAncestor participating.
*/
static bool
Participate3DContextFrame(nsIFrame* aAncestor, nsIFrame* aDescendant) {
FrameParticipatesIn3DContext(nsIFrame* aAncestor, nsIFrame* aDescendant) {
MOZ_ASSERT(aAncestor != aDescendant);
MOZ_ASSERT(aAncestor->Extend3DContext());
nsIFrame* frame;
@ -1910,6 +1910,20 @@ Participate3DContextFrame(nsIFrame* aAncestor, nsIFrame* aDescendant) {
return true;
}
static bool
ItemParticipatesIn3DContext(nsIFrame* aAncestor, nsDisplayItem* aItem)
{
nsIFrame* transformFrame;
if (aItem->GetType() == nsDisplayItem::TYPE_TRANSFORM) {
transformFrame = aItem->Frame();
} else if (aItem->GetType() == nsDisplayItem::TYPE_PERSPECTIVE) {
transformFrame = static_cast<nsDisplayPerspective*>(aItem)->TransformFrame();
} else {
return false;
}
return FrameParticipatesIn3DContext(aAncestor, transformFrame);
}
static void
WrapSeparatorTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsRect& aDirtyRect,
@ -2035,6 +2049,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex perspectiveIndex(aBuilder, this);
if (isTransformed || useBlendMode || usingSVGEffects || useStickyPosition) {
// We don't need to pass ancestor clipping down to our children;
// everything goes inside a display item's child list, and the display
@ -2190,8 +2206,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
int index = 1;
while (nsDisplayItem* item = resultList.RemoveBottom()) {
if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
Participate3DContextFrame(this, item->Frame())) {
if (ItemParticipatesIn3DContext(this, item)) {
// The frame of this item participates the same 3D context.
WrapSeparatorTransform(aBuilder, this, dirtyRect,
&nonparticipants, &participants, index++);
@ -2213,7 +2228,9 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
}
// Restore clip state now so nsDisplayTransform is clipped properly.
clipState.Restore();
if (!HasPerspective()) {
clipState.Restore();
}
// Revert to the dirtyrect coming in from the parent, without our transform
// taken into account.
buildingDisplayList.SetDirtyRect(dirtyRectOutsideTransform);
@ -2228,6 +2245,15 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
nsDisplayTransform *transformItem =
new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList, dirtyRect);
resultList.AppendNewToTop(transformItem);
if (HasPerspective()) {
clipState.Restore();
resultList.AppendNewToTop(
new (aBuilder) nsDisplayPerspective(
aBuilder, this,
GetContainingBlock()->GetContent()->GetPrimaryFrame(), &resultList));
}
}
/* If we're doing VR rendering, then we need to wrap everything in a nsDisplayVR
@ -2287,7 +2313,8 @@ WrapInWrapList(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayList* aList)
{
nsDisplayItem* item = aList->GetBottom();
if (!item || item->GetAbove() || item->Frame() != aFrame) {
if (!item || item->GetAbove() ||
(item->Frame() != aFrame && item->GetType() != nsDisplayItem::TYPE_PERSPECTIVE)) {
return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList);
}
aList->RemoveBottom();
@ -5027,7 +5054,7 @@ nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel();
Matrix4x4 result = nsDisplayTransform::GetResultingTransformMatrix(this,
nsPoint(0, 0), scaleFactor, 0,
nsPoint(0, 0), scaleFactor, nsDisplayTransform::INCLUDE_PERSPECTIVE,
nullptr, aOutAncestor);
// XXXjwatt: seems like this will double count offsets in the face of preserve-3d:
nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);