Bug 952011 - Use Untransform API to safetly untransform points when we have a projective matrix. r=roc

This commit is contained in:
Matt Woodrow 2014-01-29 13:10:35 +13:00
parent e42d6dabd3
commit 0d7db1c1e0
5 changed files with 73 additions and 23 deletions

View File

@ -1561,7 +1561,10 @@ SetVisibleRegionForLayer(Layer* aLayer, const nsIntRegion& aLayerVisibleRegion,
// for the layer, so it doesn't really matter what we do here
gfxRect itemVisible(aRestrictToRect.x, aRestrictToRect.y,
aRestrictToRect.width, aRestrictToRect.height);
gfxRect layerVisible = transform.Inverse().ProjectRectBounds(itemVisible);
nsIntRect childBounds = aLayerVisibleRegion.GetBounds();
gfxRect childGfxBounds(childBounds.x, childBounds.y,
childBounds.width, childBounds.height);
gfxRect layerVisible = transform.UntransformBounds(itemVisible, childGfxBounds);
layerVisible.RoundOut();
nsIntRect visibleRect;

View File

@ -1400,7 +1400,7 @@ void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
if (aRect.width != 1 || aRect.height != 1) {
point = aRect.Center();
}
temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(point)));
temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
writeFrames = &temp[temp.Length() - 1].mFrames;
}
} else {
@ -4460,7 +4460,7 @@ bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder,
* If we can't untransform, take the entire overflow rect */
nsRect untransformedVisibleRect;
if (ShouldPrerenderTransformedContent(aBuilder, mFrame) ||
!UntransformVisibleRect(&untransformedVisibleRect))
!UntransformVisibleRect(aBuilder, &untransformedVisibleRect))
{
untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf();
}
@ -4501,14 +4501,24 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder,
* Thus we have to invert the matrix, which normally does
* the reverse operation (e.g. regular->transformed)
*/
bool snap;
nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
NSAppUnitsToFloatPixels(childBounds.y, factor),
NSAppUnitsToFloatPixels(childBounds.width, factor),
NSAppUnitsToFloatPixels(childBounds.height, factor));
/* Now, apply the transform and pass it down the channel. */
nsRect resultingRect;
if (aRect.width == 1 && aRect.height == 1) {
// Magic width/height indicating we're hit testing a point, not a rect
gfxPoint point = matrix.Inverse().ProjectPoint(
gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor),
NSAppUnitsToFloatPixels(aRect.y, factor)));
gfxPoint point;
if (!matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor),
NSAppUnitsToFloatPixels(aRect.y, factor)),
childGfxBounds,
&point)) {
return;
}
resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor),
NSFloatPixelsToAppUnits(float(point.y), factor),
@ -4520,7 +4530,7 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder,
NSAppUnitsToFloatPixels(aRect.width, factor),
NSAppUnitsToFloatPixels(aRect.height, factor));
gfxRect rect = matrix.Inverse().ProjectRectBounds(originalRect);;
gfxRect rect = matrix.UntransformBounds(originalRect, childGfxBounds);
resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
NSFloatPixelsToAppUnits(float(rect.Y()), factor),
@ -4528,6 +4538,10 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder,
NSFloatPixelsToAppUnits(float(rect.Height()), factor));
}
if (resultingRect.IsEmpty()) {
return;
}
#ifdef DEBUG_HIT
printf("Frame: %p\n", dynamic_cast<void *>(mFrame));
@ -4547,7 +4561,7 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder,
}
float
nsDisplayTransform::GetHitDepthAtPoint(const nsPoint& aPoint)
nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint)
{
// GetTransform always operates in dev pixels.
float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
@ -4555,9 +4569,19 @@ nsDisplayTransform::GetHitDepthAtPoint(const nsPoint& aPoint)
NS_ASSERTION(IsFrameVisible(mFrame, matrix), "We can't have hit a frame that isn't visible!");
gfxPoint point =
matrix.Inverse().ProjectPoint(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, factor),
NSAppUnitsToFloatPixels(aPoint.y, factor)));
bool snap;
nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
NSAppUnitsToFloatPixels(childBounds.y, factor),
NSAppUnitsToFloatPixels(childBounds.width, factor),
NSAppUnitsToFloatPixels(childBounds.height, factor));
gfxPoint point;
DebugOnly<bool> result = matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, factor),
NSAppUnitsToFloatPixels(aPoint.y, factor)),
childGfxBounds,
&point);
NS_ASSERTION(result, "Why are we trying to get the depth for a point we didn't hit?");
gfxPoint3D transformed = matrix.Transform3D(gfxPoint3D(point.x, point.y, 0));
return transformed.z;
@ -4608,7 +4632,7 @@ nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder,
// updated extremely cheaply, without invalidating any other
// content.
if (ShouldPrerenderTransformedContent(aBuilder, mFrame) ||
!UntransformVisibleRect(&untransformedVisible)) {
!UntransformVisibleRect(aBuilder, &untransformedVisible)) {
return nsRegion();
}
@ -4632,7 +4656,7 @@ nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder,
bool nsDisplayTransform::IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor)
{
nsRect untransformedVisible;
if (!UntransformVisibleRect(&untransformedVisible)) {
if (!UntransformVisibleRect(aBuilder, &untransformedVisible)) {
return false;
}
const gfx3DMatrix& matrix = GetTransform();
@ -4728,7 +4752,8 @@ nsRect nsDisplayTransform::TransformRectOut(const nsRect &aUntransformedBounds,
factor);
}
bool nsDisplayTransform::UntransformVisibleRect(nsRect *aOutRect)
bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
nsRect *aOutRect)
{
const gfx3DMatrix& matrix = GetTransform();
if (matrix.IsSingular())
@ -4741,8 +4766,14 @@ bool nsDisplayTransform::UntransformVisibleRect(nsRect *aOutRect)
NSAppUnitsToFloatPixels(mVisibleRect.width, factor),
NSAppUnitsToFloatPixels(mVisibleRect.height, factor));
/* We want to untransform the matrix, so invert the transformation first! */
result = matrix.Inverse().ProjectRectBounds(result);
bool snap;
nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
NSAppUnitsToFloatPixels(childBounds.y, factor),
NSAppUnitsToFloatPixels(childBounds.width, factor),
NSAppUnitsToFloatPixels(childBounds.height, factor));
result = matrix.UntransformBounds(result, childGfxBounds);
*aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor);

View File

@ -3051,7 +3051,7 @@ public:
const gfx3DMatrix& GetTransform();
float GetHitDepthAtPoint(const nsPoint& aPoint);
float GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint);
/**
* TransformRect takes in as parameters a rectangle (in aFrame's coordinate
@ -3084,7 +3084,8 @@ public:
/* UntransformRect is like TransformRect, except that it inverts the
* transform.
*/
bool UntransformVisibleRect(nsRect* aOutRect);
bool UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
nsRect* aOutRect);
static gfxPoint3D GetDeltaToTransformOrigin(const nsIFrame* aFrame,
float aAppUnitsPerPixel,

View File

@ -1833,13 +1833,21 @@ nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame,
return true;
}
static gfxPoint
static bool
TransformGfxPointFromAncestor(nsIFrame *aFrame,
const gfxPoint &aPoint,
nsIFrame *aAncestor)
nsIFrame *aAncestor,
gfxPoint* aOut)
{
gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
return ctm.Inverse().ProjectPoint(aPoint);
float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
nsRect childBounds = aFrame->GetVisualOverflowRectRelativeToSelf();
gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
NSAppUnitsToFloatPixels(childBounds.y, factor),
NSAppUnitsToFloatPixels(childBounds.width, factor),
NSAppUnitsToFloatPixels(childBounds.height, factor));
return ctm.UntransformPoint(aPoint, childGfxBounds, aOut);
}
static gfxRect
@ -1881,10 +1889,14 @@ nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame* aFrame,
NSAppUnitsToFloatPixels(aPoint.y, factor));
if (text) {
result = TransformGfxPointFromAncestor(text, result, aAncestor);
if (!TransformGfxPointFromAncestor(text, result, aAncestor, &result)) {
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
}
result = text->TransformFramePointToTextChild(result, aFrame);
} else {
result = TransformGfxPointFromAncestor(aFrame, result, nullptr);
if (!TransformGfxPointFromAncestor(aFrame, result, nullptr, &result)) {
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
}
}
return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),

View File

@ -698,6 +698,9 @@ public:
/**
* Transform aPoint relative to aAncestor down to the coordinate system of
* aFrame.
*
* Returns nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if no equivalent
* point exists in the child frame (can happen with projective transforms).
*/
static nsPoint TransformAncestorPointToFrame(nsIFrame* aFrame,
const nsPoint& aPoint,