1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Matt Woodrow <mwoodrow@mozilla.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "LayerSorter.h"
39 : #include "DirectedGraph.h"
40 : #include "limits.h"
41 : #include "gfxLineSegment.h"
42 :
43 : namespace mozilla {
44 : namespace layers {
45 :
46 : enum LayerSortOrder {
47 : Undefined,
48 : ABeforeB,
49 : BBeforeA,
50 : };
51 :
52 : /**
53 : * Recover the z component from a 2d transformed point by finding the intersection
54 : * of a line through the point in the z direction and the transformed plane.
55 : *
56 : * We want to solve:
57 : *
58 : * point = normal . (p0 - l0) / normal . l
59 : */
60 0 : static gfxFloat RecoverZDepth(const gfx3DMatrix& aTransform, const gfxPoint& aPoint)
61 : {
62 0 : const gfxPoint3D l(0, 0, 1);
63 0 : gfxPoint3D l0 = gfxPoint3D(aPoint.x, aPoint.y, 0);
64 0 : gfxPoint3D p0 = aTransform.Transform3D(gfxPoint3D(0, 0, 0));
65 0 : gfxPoint3D normal = aTransform.GetNormalVector();
66 :
67 0 : gfxFloat n = normal.DotProduct(p0 - l0);
68 0 : gfxFloat d = normal.DotProduct(l);
69 :
70 0 : if (!d) {
71 0 : return 0;
72 : }
73 :
74 0 : return n/d;
75 : }
76 :
77 : /**
78 : * Determine if this transform layer should be drawn before another when they
79 : * are both preserve-3d children.
80 : *
81 : * We want to find the relative z depths of the 2 layers at points where they
82 : * intersect when projected onto the 2d screen plane. Intersections are defined
83 : * as corners that are positioned within the other quad, as well as intersections
84 : * of the lines.
85 : *
86 : * We then choose the intersection point with the greatest difference in Z
87 : * depths and use this point to determine an ordering for the two layers.
88 : * For layers that are intersecting in 3d space, this essentially guesses an
89 : * order. In a lot of cases we only intersect right at the edge point (3d cubes
90 : * in particular) and this generates the 'correct' looking ordering. For planes
91 : * that truely intersect, then there is no correct ordering and this remains
92 : * unsolved without changing our rendering code.
93 : */
94 0 : static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
95 0 : gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds();
96 0 : gfxRect otherRect = aTwo->GetEffectiveVisibleRegion().GetBounds();
97 :
98 0 : gfx3DMatrix ourTransform = aOne->GetTransform();
99 0 : gfx3DMatrix otherTransform = aTwo->GetTransform();
100 :
101 : // Transform both rectangles and project into 2d space.
102 0 : gfxQuad ourTransformedRect = ourTransform.TransformRect(ourRect);
103 0 : gfxQuad otherTransformedRect = otherTransform.TransformRect(otherRect);
104 :
105 0 : gfxRect ourBounds = ourTransformedRect.GetBounds();
106 0 : gfxRect otherBounds = otherTransformedRect.GetBounds();
107 :
108 0 : if (!ourBounds.Intersects(otherBounds)) {
109 0 : return Undefined;
110 : }
111 :
112 : // Make a list of all points that are within the other rect.
113 : // Could we just check Contains() on the bounds rects. ie, is it possible
114 : // for layers to overlap without intersections (in 2d space) and yet still
115 : // have their bounds rects not completely enclose each other?
116 0 : nsTArray<gfxPoint> points;
117 0 : for (PRUint32 i = 0; i < 4; i++) {
118 0 : if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) {
119 0 : points.AppendElement(otherTransformedRect.mPoints[i]);
120 : }
121 0 : if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) {
122 0 : points.AppendElement(ourTransformedRect.mPoints[i]);
123 : }
124 : }
125 :
126 : // Look for intersections between lines (in 2d space) and use these as
127 : // depth testing points.
128 0 : for (PRUint32 i = 0; i < 4; i++) {
129 0 : for (PRUint32 j = 0; j < 4; j++) {
130 0 : gfxPoint intersection;
131 : gfxLineSegment one(ourTransformedRect.mPoints[i],
132 0 : ourTransformedRect.mPoints[(i + 1) % 4]);
133 : gfxLineSegment two(otherTransformedRect.mPoints[j],
134 0 : otherTransformedRect.mPoints[(j + 1) % 4]);
135 0 : if (one.Intersects(two, intersection)) {
136 0 : points.AppendElement(intersection);
137 : }
138 : }
139 : }
140 :
141 : // No intersections, no defined order between these layers.
142 0 : if (points.IsEmpty()) {
143 0 : return Undefined;
144 : }
145 :
146 : // Find the relative Z depths of each intersection point and check that the layers are in the same order.
147 0 : gfxFloat highest = 0;
148 0 : for (PRUint32 i = 0; i < points.Length(); i++) {
149 0 : gfxFloat ourDepth = RecoverZDepth(ourTransform, points.ElementAt(i));
150 0 : gfxFloat otherDepth = RecoverZDepth(otherTransform, points.ElementAt(i));
151 :
152 0 : gfxFloat difference = otherDepth - ourDepth;
153 :
154 0 : if (fabs(difference) > fabs(highest)) {
155 0 : highest = difference;
156 : }
157 : }
158 : // If layers have the same depth keep the original order
159 0 : if (highest >= 0) {
160 0 : return ABeforeB;
161 : } else {
162 0 : return BBeforeA;
163 : }
164 : }
165 :
166 : #ifdef DEBUG
167 1464 : static bool gDumpLayerSortList = getenv("MOZ_DUMP_LAYER_SORT_LIST") != 0;
168 :
169 0 : static void DumpLayerList(nsTArray<Layer*>& aLayers)
170 : {
171 0 : for (PRUint32 i = 0; i < aLayers.Length(); i++) {
172 0 : fprintf(stderr, "%p, ", aLayers.ElementAt(i));
173 : }
174 0 : fprintf(stderr, "\n");
175 0 : }
176 :
177 0 : static void DumpEdgeList(DirectedGraph<Layer*>& aGraph)
178 : {
179 0 : nsTArray<DirectedGraph<Layer*>::Edge> edges = aGraph.GetEdgeList();
180 :
181 0 : for (PRUint32 i = 0; i < edges.Length(); i++) {
182 0 : fprintf(stderr, "From: %p, To: %p\n", edges.ElementAt(i).mFrom, edges.ElementAt(i).mTo);
183 : }
184 0 : }
185 : #endif
186 :
187 : // The maximum number of layers that we will attempt to sort. Anything
188 : // greater than this will be left unsorted. We should consider enabling
189 : // depth buffering for the scene in this case.
190 : #define MAX_SORTABLE_LAYERS 100
191 :
192 0 : void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
193 : {
194 0 : PRUint32 nodeCount = aLayers.Length();
195 0 : if (nodeCount > MAX_SORTABLE_LAYERS) {
196 0 : return;
197 : }
198 0 : DirectedGraph<Layer*> graph;
199 :
200 : #ifdef DEBUG
201 0 : if (gDumpLayerSortList) {
202 0 : fprintf(stderr, " --- Layers before sorting: --- \n");
203 0 : DumpLayerList(aLayers);
204 : }
205 : #endif
206 :
207 : // Iterate layers and determine edges.
208 0 : for (PRUint32 i = 0; i < nodeCount; i++) {
209 0 : for (PRUint32 j = i + 1; j < nodeCount; j++) {
210 0 : Layer* a = aLayers.ElementAt(i);
211 0 : Layer* b = aLayers.ElementAt(j);
212 0 : LayerSortOrder order = CompareDepth(a, b);
213 0 : if (order == ABeforeB) {
214 0 : graph.AddEdge(a, b);
215 0 : } else if (order == BBeforeA) {
216 0 : graph.AddEdge(b, a);
217 : }
218 : }
219 : }
220 :
221 : #ifdef DEBUG
222 0 : if (gDumpLayerSortList) {
223 0 : fprintf(stderr, " --- Edge List: --- \n");
224 0 : DumpEdgeList(graph);
225 : }
226 : #endif
227 :
228 : // Build a new array using the graph.
229 0 : nsTArray<Layer*> noIncoming;
230 0 : nsTArray<Layer*> sortedList;
231 :
232 : // Make a list of all layers with no incoming edges.
233 0 : noIncoming.AppendElements(aLayers);
234 0 : const nsTArray<DirectedGraph<Layer*>::Edge>& edges = graph.GetEdgeList();
235 0 : for (PRUint32 i = 0; i < edges.Length(); i++) {
236 0 : noIncoming.RemoveElement(edges.ElementAt(i).mTo);
237 : }
238 :
239 : // Move each item without incoming edges into the sorted list,
240 : // and remove edges from it.
241 0 : do {
242 0 : if (!noIncoming.IsEmpty()) {
243 0 : PRUint32 last = noIncoming.Length() - 1;
244 :
245 0 : Layer* layer = noIncoming.ElementAt(last);
246 :
247 0 : noIncoming.RemoveElementAt(last);
248 0 : sortedList.AppendElement(layer);
249 :
250 0 : nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
251 0 : graph.GetEdgesFrom(layer, outgoing);
252 0 : for (PRUint32 i = 0; i < outgoing.Length(); i++) {
253 0 : DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
254 0 : graph.RemoveEdge(edge);
255 0 : if (!graph.NumEdgesTo(edge.mTo)) {
256 : // If this node also has no edges now, add it to the list
257 0 : noIncoming.AppendElement(edge.mTo);
258 : }
259 : }
260 : }
261 :
262 : // If there are no nodes without incoming edges, but there
263 : // are still edges, then we have a cycle.
264 0 : if (noIncoming.IsEmpty() && graph.GetEdgeCount()) {
265 : // Find the node with the least incoming edges.
266 0 : PRUint32 minEdges = UINT_MAX;
267 0 : Layer* minNode = nsnull;
268 0 : for (PRUint32 i = 0; i < aLayers.Length(); i++) {
269 0 : PRUint32 edgeCount = graph.NumEdgesTo(aLayers.ElementAt(i));
270 0 : if (edgeCount && edgeCount < minEdges) {
271 0 : minEdges = edgeCount;
272 0 : minNode = aLayers.ElementAt(i);
273 0 : if (minEdges == 1) {
274 0 : break;
275 : }
276 : }
277 : }
278 :
279 : // Remove all of them!
280 0 : graph.RemoveEdgesTo(minNode);
281 0 : noIncoming.AppendElement(minNode);
282 : }
283 0 : } while (!noIncoming.IsEmpty());
284 0 : NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
285 : #ifdef DEBUG
286 0 : if (gDumpLayerSortList) {
287 0 : fprintf(stderr, " --- Layers after sorting: --- \n");
288 0 : DumpLayerList(sortedList);
289 : }
290 : #endif
291 :
292 0 : aLayers.Clear();
293 0 : aLayers.AppendElements(sortedList);
294 : }
295 :
296 : }
297 4392 : }
|