зеркало из https://github.com/mozilla/pjs.git
Bug 684759 - Part 3a - Sort preserve-3d layers using their z depth at points where they intersect in 2d space. r=roc
This commit is contained in:
Родитель
c45a16d1df
Коммит
224fe2d2f1
|
@ -0,0 +1,258 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** 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 Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* 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 ***** */
|
||||
|
||||
#include "LayerSorter.h"
|
||||
#include "DirectedGraph.h"
|
||||
#include "limits.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
enum LayerSortOrder {
|
||||
Undefined,
|
||||
ABeforeB,
|
||||
BBeforeA,
|
||||
};
|
||||
|
||||
/**
|
||||
* Recover the z component from a 2d transformed point by finding the intersection
|
||||
* of a line through the point in the z direction and the transformed plane.
|
||||
*
|
||||
* We want to solve:
|
||||
*
|
||||
* point = normal . (p0 - l0) / normal . l
|
||||
*/
|
||||
static gfxFloat RecoverZDepth(const gfx3DMatrix& aTransform, const gfxPoint& aPoint)
|
||||
{
|
||||
const gfxPoint3D l(0, 0, 1);
|
||||
gfxPoint3D l0 = gfxPoint3D(aPoint.x, aPoint.y, 0);
|
||||
gfxPoint3D p0 = aTransform.Transform3D(gfxPoint3D(0, 0, 0));
|
||||
gfxPoint3D normal = aTransform.GetNormalVector();
|
||||
|
||||
gfxFloat n = normal.DotProduct(p0 - l0);
|
||||
gfxFloat d = normal.DotProduct(l);
|
||||
|
||||
if (!d) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return n/d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this transform layer should be drawn before another when they
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
|
||||
gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds();
|
||||
gfxRect otherRect = aTwo->GetEffectiveVisibleRegion().GetBounds();
|
||||
|
||||
gfx3DMatrix ourTransform = aOne->GetTransform();
|
||||
gfx3DMatrix otherTransform = aTwo->GetTransform();
|
||||
|
||||
// Transform both rectangles and project into 2d space.
|
||||
gfxQuad ourTransformedRect = ourTransform.TransformRect(ourRect);
|
||||
gfxQuad otherTransformedRect = otherTransform.TransformRect(otherRect);
|
||||
|
||||
// Make a list of all points that are within the other rect.
|
||||
nsTArray<gfxPoint> points;
|
||||
for (PRUint32 i=0; i<4; i++) {
|
||||
if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) {
|
||||
points.AppendElement(otherTransformedRect.mPoints[i]);
|
||||
}
|
||||
if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) {
|
||||
points.AppendElement(ourTransformedRect.mPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// No intersections, no defined order between these layers.
|
||||
if (points.IsEmpty()) {
|
||||
return Undefined;
|
||||
}
|
||||
|
||||
// Find the relative Z depths of each intersection point and check that the layers are in the same order.
|
||||
bool drawBefore = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (drawBefore) {
|
||||
return ABeforeB;
|
||||
}
|
||||
return BBeforeA;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool gDumpLayerSortList = getenv("MOZ_DUMP_LAYER_SORT_LIST") != 0;
|
||||
|
||||
static void DumpLayerList(nsTArray<Layer*>& aLayers)
|
||||
{
|
||||
for (PRUint32 i = 0; i < aLayers.Length(); i++) {
|
||||
fprintf(stderr, "%p, ", aLayers.ElementAt(i));
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
static void DumpEdgeList(DirectedGraph<Layer*>& aGraph)
|
||||
{
|
||||
nsTArray<DirectedGraph<Layer*>::Edge> edges = aGraph.GetEdgeList();
|
||||
|
||||
for (PRUint32 i = 0; i < edges.Length(); i++) {
|
||||
fprintf(stderr, "From: %p, To: %p\n", edges.ElementAt(i).mFrom, edges.ElementAt(i).mTo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// The maximum number of layers that we will attempt to sort. Anything
|
||||
// greater than this will be left unsorted. We should consider enabling
|
||||
// depth buffering for the scene in this case.
|
||||
#define MAX_SORTABLE_LAYERS 100
|
||||
|
||||
void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
|
||||
{
|
||||
PRUint32 nodeCount = aLayers.Length();
|
||||
if (nodeCount > MAX_SORTABLE_LAYERS) {
|
||||
return;
|
||||
}
|
||||
DirectedGraph<Layer*> graph;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (gDumpLayerSortList) {
|
||||
fprintf(stderr, " --- Layers before sorting: --- \n");
|
||||
DumpLayerList(aLayers);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Iterate layers and determine edges.
|
||||
for (PRUint32 i = 0; i < nodeCount; i++) {
|
||||
for (PRUint32 j = i + 1; j < nodeCount; j++) {
|
||||
Layer* a = aLayers.ElementAt(i);
|
||||
Layer* b = aLayers.ElementAt(j);
|
||||
LayerSortOrder order = CompareDepth(a, b);
|
||||
if (order == ABeforeB) {
|
||||
graph.AddEdge(a, b);
|
||||
} else if (order == BBeforeA) {
|
||||
graph.AddEdge(b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (gDumpLayerSortList) {
|
||||
fprintf(stderr, " --- Edge List: --- \n");
|
||||
DumpEdgeList(graph);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Build a new array using the graph.
|
||||
nsTArray<Layer*> noIncoming;
|
||||
nsTArray<Layer*> sortedList;
|
||||
|
||||
// Make a list of all layers with no incoming edges.
|
||||
noIncoming.AppendElements(aLayers);
|
||||
const nsTArray<DirectedGraph<Layer*>::Edge>& edges = graph.GetEdgeList();
|
||||
for (PRUint32 i = 0; i < edges.Length(); i++) {
|
||||
noIncoming.RemoveElement(edges.ElementAt(i).mTo);
|
||||
}
|
||||
|
||||
// Move each item without incoming edges into the sorted list,
|
||||
// and remove edges from it.
|
||||
while (!noIncoming.IsEmpty()) {
|
||||
PRUint32 last = noIncoming.Length() - 1;
|
||||
|
||||
Layer* layer = noIncoming.ElementAt(last);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no nodes without incoming edges, but there
|
||||
// are still edges, then we have a cycle.
|
||||
if (noIncoming.IsEmpty() && graph.GetEdgeCount()) {
|
||||
// Find the node with the least incoming edges.
|
||||
PRUint32 minEdges = UINT_MAX;
|
||||
Layer* minNode = nsnull;
|
||||
for (PRUint32 i = 0; i < aLayers.Length(); i++) {
|
||||
PRUint32 edgeCount = graph.NumEdgesTo(aLayers.ElementAt(i));
|
||||
if (edgeCount && edgeCount < minEdges) {
|
||||
minEdges = edgeCount;
|
||||
minNode = aLayers.ElementAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all of them!
|
||||
graph.RemoveEdgesTo(minNode);
|
||||
noIncoming.AppendElement(minNode);
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
|
||||
#ifdef DEBUG
|
||||
if (gDumpLayerSortList) {
|
||||
fprintf(stderr, " --- Layers after sorting: --- \n");
|
||||
DumpLayerList(sortedList);
|
||||
}
|
||||
#endif
|
||||
|
||||
aLayers.Clear();
|
||||
aLayers.AppendElements(sortedList);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** 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 Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* 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_LAYERSORTER_H
|
||||
#define GFX_LAYERSORTER_H
|
||||
|
||||
#include "Layers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers);
|
||||
|
||||
}
|
||||
}
|
||||
#endif /* GFX_LAYERSORTER_H */
|
|
@ -48,6 +48,7 @@
|
|||
#include "gfxUtils.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "mozilla/Util.h"
|
||||
#include "LayerSorter.h"
|
||||
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::gfx;
|
||||
|
@ -415,6 +416,29 @@ ContainerLayer::HasMultipleChildren()
|
|||
return PR_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
|
||||
{
|
||||
nsAutoTArray<Layer*, 10> toSort;
|
||||
|
||||
for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
|
||||
ContainerLayer* container = l->AsContainerLayer();
|
||||
if (container && container->GetContentFlags() & CONTENT_PRESERVE_3D) {
|
||||
toSort.AppendElement(l);
|
||||
} else {
|
||||
if (toSort.Length() > 0) {
|
||||
SortLayersBy3DZOrder(toSort);
|
||||
aArray.MoveElementsFrom(toSort);
|
||||
}
|
||||
aArray.AppendElement(l);
|
||||
}
|
||||
}
|
||||
if (toSort.Length() > 0) {
|
||||
SortLayersBy3DZOrder(toSort);
|
||||
aArray.MoveElementsFrom(toSort);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContainerLayer::DefaultComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
|
||||
{
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "gfx3DMatrix.h"
|
||||
#include "gfxColor.h"
|
||||
#include "gfxPattern.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
|
@ -577,7 +578,13 @@ public:
|
|||
* paint time.
|
||||
* This should never be set at the same time as CONTENT_OPAQUE.
|
||||
*/
|
||||
CONTENT_COMPONENT_ALPHA = 0x02
|
||||
CONTENT_COMPONENT_ALPHA = 0x02,
|
||||
|
||||
/**
|
||||
* If this is set then this layer is part of a preserve-3d group, and should
|
||||
* be sorted with sibling layers that are also part of the same group.
|
||||
*/
|
||||
CONTENT_PRESERVE_3D = 0x04
|
||||
};
|
||||
/**
|
||||
* CONSTRUCTION PHASE ONLY
|
||||
|
@ -1095,6 +1102,8 @@ public:
|
|||
|
||||
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs);
|
||||
|
||||
void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray);
|
||||
|
||||
// These getters can be used anytime.
|
||||
|
||||
virtual ContainerLayer* AsContainerLayer() { return this; }
|
||||
|
|
|
@ -66,6 +66,7 @@ EXPORTS = \
|
|||
LayerManagerOGL.h \
|
||||
LayerManagerOGLProgram.h \
|
||||
ReadbackLayer.h \
|
||||
LayerSorter.h \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
|
@ -80,6 +81,7 @@ CPPSRCS = \
|
|||
ImageLayerOGL.cpp \
|
||||
LayerManagerOGL.cpp \
|
||||
ThebesLayerOGL.cpp \
|
||||
LayerSorter.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
|
||||
|
|
|
@ -762,24 +762,6 @@ static bool IsContentLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
|
|||
static_cast<nsIContent*>(aClosure)) <= 0;
|
||||
}
|
||||
|
||||
static bool IsZPositionLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
|
||||
void* aClosure) {
|
||||
if (!aItem1->GetUnderlyingFrame()->Preserves3D() ||
|
||||
!aItem1->GetUnderlyingFrame()->Preserves3D()) {
|
||||
return IsContentLEQ(aItem1, aItem2, aClosure);
|
||||
}
|
||||
|
||||
nsIFrame* ancestor;
|
||||
gfx3DMatrix matrix1 = aItem1->GetUnderlyingFrame()->GetTransformMatrix(&ancestor);
|
||||
gfx3DMatrix matrix2 = aItem2->GetUnderlyingFrame()->GetTransformMatrix(&ancestor);
|
||||
|
||||
if (matrix1._43 == matrix2._43) {
|
||||
return IsContentLEQ(aItem1, aItem2, aClosure);
|
||||
}
|
||||
|
||||
return matrix1._43 < matrix2._43;
|
||||
}
|
||||
|
||||
static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
|
||||
void* aClosure) {
|
||||
// These GetUnderlyingFrame calls return non-null because we're only used
|
||||
|
@ -788,7 +770,7 @@ static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
|
|||
PRInt32 index1 = nsLayoutUtils::GetZIndex(aItem1->GetUnderlyingFrame());
|
||||
PRInt32 index2 = nsLayoutUtils::GetZIndex(aItem2->GetUnderlyingFrame());
|
||||
if (index1 == index2)
|
||||
return IsZPositionLEQ(aItem1, aItem2, aClosure);
|
||||
return IsContentLEQ(aItem1, aItem2, aClosure);
|
||||
return index1 < index2;
|
||||
}
|
||||
|
||||
|
@ -835,11 +817,6 @@ void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder,
|
|||
Sort(aBuilder, IsContentLEQ, aCommonAncestor);
|
||||
}
|
||||
|
||||
void nsDisplayList::SortByZPosition(nsDisplayListBuilder* aBuilder,
|
||||
nsIContent* aCommonAncestor) {
|
||||
Sort(aBuilder, IsZPositionLEQ, aCommonAncestor);
|
||||
}
|
||||
|
||||
void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder,
|
||||
SortLEQ aCmp, void* aClosure) {
|
||||
ExplodeAnonymousChildLists(aBuilder);
|
||||
|
@ -2536,9 +2513,16 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu
|
|||
return nsnull;
|
||||
}
|
||||
|
||||
return aBuilder->LayerBuilder()->
|
||||
nsRefPtr<ContainerLayer> container = aBuilder->LayerBuilder()->
|
||||
BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetList(),
|
||||
aContainerParameters, &newTransformMatrix);
|
||||
|
||||
// Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags,
|
||||
// so we never need to explicitely unset this flag.
|
||||
if (mFrame->Preserves3D()) {
|
||||
container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D);
|
||||
}
|
||||
return container.forget();
|
||||
}
|
||||
|
||||
nsDisplayItem::LayerState
|
||||
|
@ -2546,7 +2530,7 @@ nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
|
|||
LayerManager* aManager) {
|
||||
if (mFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer))
|
||||
return LAYER_ACTIVE;
|
||||
if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D())
|
||||
if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D() || mFrame->Preserves3D())
|
||||
return LAYER_ACTIVE;
|
||||
nsIFrame* activeScrolledRoot =
|
||||
nsLayoutUtils::GetActiveScrolledRootFor(mFrame, nsnull);
|
||||
|
|
|
@ -1012,8 +1012,6 @@ public:
|
|||
*/
|
||||
void SortByContentOrder(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor);
|
||||
|
||||
void SortByZPosition(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor);
|
||||
|
||||
/**
|
||||
* Generic stable sort. Take care, because some of the items might be nsDisplayLists
|
||||
* themselves.
|
||||
|
|
Загрузка…
Ссылка в новой задаче