зеркало из https://github.com/mozilla/pjs.git
Bug 693521 - Improve preserve-3d sorting behaviour by using line intersection points. r=roc
This commit is contained in:
Родитель
624fd412bb
Коммит
231796bcb0
|
@ -38,6 +38,7 @@
|
|||
#include "LayerSorter.h"
|
||||
#include "DirectedGraph.h"
|
||||
#include "limits.h"
|
||||
#include "gfxLineSegment.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
@ -78,11 +79,17 @@ static gfxFloat RecoverZDepth(const gfx3DMatrix& aTransform, const gfxPoint& aPo
|
|||
* are both preserve-3d children.
|
||||
*
|
||||
* We want to find the relative z depths of the 2 layers at points where they
|
||||
* intersect when projected onto the 2d screen plane.
|
||||
* intersect when projected onto the 2d screen plane. Intersections are defined
|
||||
* as corners that are positioned within the other quad, as well as intersections
|
||||
* of the lines.
|
||||
*
|
||||
* If the ordering is consistent at all intersection points, then we have
|
||||
* a definitive order, otherwise the 2 layers must actually intersect in 3d
|
||||
* space, and we just order these arbitrarily.
|
||||
* We then choose the intersection point with the greatest difference in Z
|
||||
* depths and use this point to determine an ordering for the two layers.
|
||||
* For layers that are intersecting in 3d space, this essentially guesses an
|
||||
* order. In a lot of cases we only intersect right at the edge point (3d cubes
|
||||
* in particular) and this generates the 'correct' looking ordering. For planes
|
||||
* that truely intersect, then there is no correct ordering and this remains
|
||||
* unsolved without changing our rendering code.
|
||||
*/
|
||||
static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
|
||||
gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds();
|
||||
|
@ -95,9 +102,19 @@ static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
|
|||
gfxQuad ourTransformedRect = ourTransform.TransformRect(ourRect);
|
||||
gfxQuad otherTransformedRect = otherTransform.TransformRect(otherRect);
|
||||
|
||||
gfxRect ourBounds = ourTransformedRect.GetBounds();
|
||||
gfxRect otherBounds = otherTransformedRect.GetBounds();
|
||||
|
||||
if (!ourBounds.Intersects(otherBounds)) {
|
||||
return Undefined;
|
||||
}
|
||||
|
||||
// Make a list of all points that are within the other rect.
|
||||
// Could we just check Contains() on the bounds rects. ie, is it possible
|
||||
// for layers to overlap without intersections (in 2d space) and yet still
|
||||
// have their bounds rects not completely enclose each other?
|
||||
nsTArray<gfxPoint> points;
|
||||
for (PRUint32 i=0; i<4; i++) {
|
||||
for (PRUint32 i = 0; i < 4; i++) {
|
||||
if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) {
|
||||
points.AppendElement(otherTransformedRect.mPoints[i]);
|
||||
}
|
||||
|
@ -105,6 +122,21 @@ static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
|
|||
points.AppendElement(ourTransformedRect.mPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Look for intersections between lines (in 2d space) and use these as
|
||||
// depth testing points.
|
||||
for (PRUint32 i = 0; i < 4; i++) {
|
||||
for (PRUint32 j = 0; j < 4; j++) {
|
||||
gfxPoint intersection;
|
||||
gfxLineSegment one(ourTransformedRect.mPoints[i],
|
||||
ourTransformedRect.mPoints[(i + 1) % 4]);
|
||||
gfxLineSegment two(otherTransformedRect.mPoints[j],
|
||||
otherTransformedRect.mPoints[(j + 1) % 4]);
|
||||
if (one.Intersects(two, intersection)) {
|
||||
points.AppendElement(intersection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No intersections, no defined order between these layers.
|
||||
if (points.IsEmpty()) {
|
||||
|
@ -112,21 +144,23 @@ static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
|
|||
}
|
||||
|
||||
// Find the relative Z depths of each intersection point and check that the layers are in the same order.
|
||||
bool drawBefore = false;
|
||||
gfxFloat highest = 0;
|
||||
for (PRUint32 i = 0; i < points.Length(); i++) {
|
||||
bool temp = RecoverZDepth(ourTransform, points.ElementAt(i)) <= RecoverZDepth(otherTransform, points.ElementAt(i));
|
||||
if (i == 0) {
|
||||
drawBefore = temp;
|
||||
} else if (drawBefore != temp) {
|
||||
// Mixed ordering means an intersection in 3d space that we can't resolve without plane splitting
|
||||
// or depth buffering. Store this as having no defined order for now.
|
||||
return Undefined;
|
||||
gfxFloat ourDepth = RecoverZDepth(ourTransform, points.ElementAt(i));
|
||||
gfxFloat otherDepth = RecoverZDepth(otherTransform, points.ElementAt(i));
|
||||
|
||||
gfxFloat difference = otherDepth - ourDepth;
|
||||
|
||||
if (fabs(difference) > fabs(highest)) {
|
||||
highest = difference;
|
||||
}
|
||||
}
|
||||
if (drawBefore) {
|
||||
// If layers have the same depth keep the original order
|
||||
if (highest >= 0) {
|
||||
return ABeforeB;
|
||||
} else {
|
||||
return BBeforeA;
|
||||
}
|
||||
return BBeforeA;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -204,22 +238,24 @@ void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
|
|||
|
||||
// Move each item without incoming edges into the sorted list,
|
||||
// and remove edges from it.
|
||||
while (!noIncoming.IsEmpty()) {
|
||||
PRUint32 last = noIncoming.Length() - 1;
|
||||
do {
|
||||
if (!noIncoming.IsEmpty()) {
|
||||
PRUint32 last = noIncoming.Length() - 1;
|
||||
|
||||
Layer* layer = noIncoming.ElementAt(last);
|
||||
Layer* layer = noIncoming.ElementAt(last);
|
||||
|
||||
noIncoming.RemoveElementAt(last);
|
||||
sortedList.AppendElement(layer);
|
||||
noIncoming.RemoveElementAt(last);
|
||||
sortedList.AppendElement(layer);
|
||||
|
||||
nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
|
||||
graph.GetEdgesFrom(layer, outgoing);
|
||||
for (PRUint32 i = 0; i < outgoing.Length(); i++) {
|
||||
DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
|
||||
graph.RemoveEdge(edge);
|
||||
if (!graph.NumEdgesTo(edge.mTo)) {
|
||||
// If this node also has no edges now, add it to the list
|
||||
noIncoming.AppendElement(edge.mTo);
|
||||
nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
|
||||
graph.GetEdgesFrom(layer, outgoing);
|
||||
for (PRUint32 i = 0; i < outgoing.Length(); i++) {
|
||||
DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
|
||||
graph.RemoveEdge(edge);
|
||||
if (!graph.NumEdgesTo(edge.mTo)) {
|
||||
// If this node also has no edges now, add it to the list
|
||||
noIncoming.AppendElement(edge.mTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,6 +270,9 @@ void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
|
|||
if (edgeCount && edgeCount < minEdges) {
|
||||
minEdges = edgeCount;
|
||||
minNode = aLayers.ElementAt(i);
|
||||
if (minEdges == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +280,7 @@ void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
|
|||
graph.RemoveEdgesTo(minNode);
|
||||
noIncoming.AppendElement(minNode);
|
||||
}
|
||||
}
|
||||
} while (!noIncoming.IsEmpty());
|
||||
NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
|
||||
#ifdef DEBUG
|
||||
if (gDumpLayerSortList) {
|
||||
|
|
|
@ -27,6 +27,7 @@ EXPORTS = \
|
|||
gfxFontUtils.h \
|
||||
gfxFontTest.h \
|
||||
gfxImageSurface.h \
|
||||
gfxLineSegment.h \
|
||||
gfxMatrix.h \
|
||||
gfxPath.h \
|
||||
gfxPattern.h \
|
||||
|
|
|
@ -677,8 +677,7 @@ gfx3DMatrix::TransformRect(const gfxRect& aRect) const
|
|||
points[2] = Transform(gfxPoint(aRect.X() + aRect.Width(),
|
||||
aRect.Y() + aRect.Height()));
|
||||
points[3] = Transform(gfxPoint(aRect.X(), aRect.Y() + aRect.Height()));
|
||||
|
||||
|
||||
|
||||
// Could this ever result in lines that intersect? I don't think so.
|
||||
return gfxQuad(points[0], points[1], points[2], points[3]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Corporation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Oracle Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2005
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Matt Woodrow <mwoodrow@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef GFX_LINESEGMENT_H
|
||||
#define GFX_LINESEGMENT_H
|
||||
|
||||
#include "gfxTypes.h"
|
||||
#include "gfxPoint.h"
|
||||
|
||||
struct THEBES_API gfxLineSegment {
|
||||
gfxLineSegment(const gfxPoint &aStart, const gfxPoint &aEnd)
|
||||
: mStart(aStart)
|
||||
, mEnd(aEnd)
|
||||
{}
|
||||
|
||||
bool PointsOnSameSide(const gfxPoint& aOne, const gfxPoint& aTwo)
|
||||
{
|
||||
// Solve the equation y - mStart.y - ((mEnd.y - mStart.y)/(mEnd.x - mStart.x))(x - mStart.x) for both points
|
||||
|
||||
gfxFloat deltaY = (mEnd.y - mStart.y);
|
||||
gfxFloat deltaX = (mEnd.x - mStart.x);
|
||||
|
||||
gfxFloat one = deltaX * (aOne.y - mStart.y) - deltaY * (aOne.x - mStart.x);
|
||||
gfxFloat two = deltaX * (aTwo.y - mStart.y) - deltaY * (aTwo.x - mStart.x);
|
||||
|
||||
// If both results have the same sign, then we're on the correct side of the line.
|
||||
// 0 (on the line) is always considered in.
|
||||
|
||||
if ((one >= 0 && two >= 0) || (one <= 0 && two <= 0))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if two line segments intersect, and returns the intersection
|
||||
* point in aIntersection if they do.
|
||||
*
|
||||
* Coincident lines are considered not intersecting as they don't have an
|
||||
* intersection point.
|
||||
*/
|
||||
bool Intersects(const gfxLineSegment& aOther, gfxPoint& aIntersection)
|
||||
{
|
||||
gfxFloat denominator = (aOther.mEnd.y - aOther.mStart.y) * (mEnd.x - mStart.x ) -
|
||||
(aOther.mEnd.x - aOther.mStart.x ) * (mEnd.y - mStart.y);
|
||||
|
||||
// Parallel or coincident. We treat coincident as not intersecting since
|
||||
// these lines are guaranteed to have corners that intersect instead.
|
||||
if (!denominator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gfxFloat anumerator = (aOther.mEnd.x - aOther.mStart.x) * (mStart.y - aOther.mStart.y) -
|
||||
(aOther.mEnd.y - aOther.mStart.y) * (mStart.x - aOther.mStart.x);
|
||||
|
||||
gfxFloat bnumerator = (mEnd.x - mStart.x) * (mStart.y - aOther.mStart.y) -
|
||||
(mEnd.y - mStart.y) * (mStart.x - aOther.mStart.x);
|
||||
|
||||
gfxFloat ua = anumerator / denominator;
|
||||
gfxFloat ub = bnumerator / denominator;
|
||||
|
||||
if (ua <= 0.0 || ua >= 1.0 ||
|
||||
ub <= 0.0 || ub >= 1.0) {
|
||||
//Intersection is outside of the segment
|
||||
return false;
|
||||
}
|
||||
|
||||
aIntersection = mStart + (mEnd - mStart) * ua;
|
||||
return true;
|
||||
}
|
||||
|
||||
gfxPoint mStart;
|
||||
gfxPoint mEnd;
|
||||
};
|
||||
|
||||
#endif /* GFX_LINESEGMENT_H */
|
|
@ -38,31 +38,8 @@
|
|||
#ifndef GFX_QUAD_H
|
||||
#define GFX_QUAD_H
|
||||
|
||||
#include "nsMathUtils.h"
|
||||
#include "mozilla/gfx/BaseSize.h"
|
||||
#include "mozilla/gfx/BasePoint.h"
|
||||
#include "nsSize.h"
|
||||
#include "nsPoint.h"
|
||||
|
||||
#include "gfxTypes.h"
|
||||
|
||||
static PRBool SameSideOfLine(const gfxPoint& aPoint1, const gfxPoint& aPoint2, const gfxPoint& aTest, const gfxPoint& aRef)
|
||||
{
|
||||
// Solve the equation y - aPoint1.y - ((aPoint2.y - aPoint1.y)/(aPoint2.x - aPoint1.x))(x - aPoint1.x) for both test and ref
|
||||
|
||||
gfxFloat deltaY = (aPoint2.y - aPoint1.y);
|
||||
gfxFloat deltaX = (aPoint2.x - aPoint1.x);
|
||||
|
||||
gfxFloat test = deltaX * (aTest.y - aPoint1.y) - deltaY * (aTest.x - aPoint1.x);
|
||||
gfxFloat ref = deltaX * (aRef.y - aPoint1.y) - deltaY * (aRef.x - aPoint1.x);
|
||||
|
||||
// If both results have the same sign, then we're on the correct side of the line.
|
||||
// 0 (on the line) is always considered in.
|
||||
|
||||
if ((test >= 0 && ref >= 0) || (test <= 0 && ref <= 0))
|
||||
return PR_TRUE;
|
||||
return PR_FALSE;
|
||||
}
|
||||
#include "gfxLineSegment.h"
|
||||
|
||||
struct THEBES_API gfxQuad {
|
||||
gfxQuad(const gfxPoint& aOne, const gfxPoint& aTwo, const gfxPoint& aThree, const gfxPoint& aFour)
|
||||
|
@ -75,10 +52,27 @@ struct THEBES_API gfxQuad {
|
|||
|
||||
PRBool Contains(const gfxPoint& aPoint)
|
||||
{
|
||||
return (SameSideOfLine(mPoints[0], mPoints[1], aPoint, mPoints[2]) &&
|
||||
SameSideOfLine(mPoints[1], mPoints[2], aPoint, mPoints[3]) &&
|
||||
SameSideOfLine(mPoints[2], mPoints[3], aPoint, mPoints[0]) &&
|
||||
SameSideOfLine(mPoints[3], mPoints[0], aPoint, mPoints[1]));
|
||||
return (gfxLineSegment(mPoints[0], mPoints[1]).PointsOnSameSide(aPoint, mPoints[2]) &&
|
||||
gfxLineSegment(mPoints[1], mPoints[2]).PointsOnSameSide(aPoint, mPoints[3]) &&
|
||||
gfxLineSegment(mPoints[2], mPoints[3]).PointsOnSameSide(aPoint, mPoints[0]) &&
|
||||
gfxLineSegment(mPoints[3], mPoints[0]).PointsOnSameSide(aPoint, mPoints[1]));
|
||||
}
|
||||
|
||||
gfxRect GetBounds()
|
||||
{
|
||||
gfxFloat min_x, max_x;
|
||||
gfxFloat min_y, max_y;
|
||||
|
||||
min_x = max_x = mPoints[0].x;
|
||||
min_y = max_y = mPoints[0].y;
|
||||
|
||||
for (int i=1; i<4; i++) {
|
||||
min_x = NS_MIN(mPoints[i].x, min_x);
|
||||
max_x = NS_MAX(mPoints[i].x, max_x);
|
||||
min_y = NS_MIN(mPoints[i].y, min_y);
|
||||
max_y = NS_MAX(mPoints[i].y, max_y);
|
||||
}
|
||||
return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y);
|
||||
}
|
||||
|
||||
gfxPoint mPoints[4];
|
||||
|
|
Загрузка…
Ссылка в новой задаче