1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "LayerSorter.h"
8 #include <math.h> // for fabs
9 #include <stdint.h> // for uint32_t
10 #include <stdio.h> // for fprintf, stderr, FILE
11 #include <stdlib.h> // for getenv
12 #include "DirectedGraph.h" // for DirectedGraph
13 #include "Layers.h" // for Layer
14 #include "gfxEnv.h" // for gfxEnv
15 #include "gfxLineSegment.h" // for gfxLineSegment
16 #include "gfxPoint.h" // for gfxPoint
17 #include "gfxQuad.h" // for gfxQuad
18 #include "gfxRect.h" // for gfxRect
19 #include "gfxTypes.h" // for gfxFloat
20 #include "gfxUtils.h" // for TransformToQuad
21 #include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D
22 #include "mozilla/Sprintf.h" // for SprintfLiteral
23 #include "nsRegion.h" // for nsIntRegion
24 #include "nsTArray.h" // for nsTArray, etc
26 #include "mozilla/Assertions.h"
31 using namespace mozilla::gfx
;
40 * Recover the z component from a 2d transformed point by finding the
41 * intersection of a line through the point in the z direction and the
46 * point = normal . (p0 - l0) / normal . l
48 static gfxFloat
RecoverZDepth(const Matrix4x4
& aTransform
,
49 const gfxPoint
& aPoint
) {
50 const Point3D
l(0, 0, 1);
51 Point3D l0
= Point3D(aPoint
.x
, aPoint
.y
, 0);
52 Point3D p0
= aTransform
.TransformPoint(Point3D(0, 0, 0));
53 Point3D normal
= aTransform
.GetNormalVector();
55 gfxFloat n
= normal
.DotProduct(p0
- l0
);
56 gfxFloat d
= normal
.DotProduct(l
);
66 * Determine if this transform layer should be drawn before another when they
67 * are both preserve-3d children.
69 * We want to find the relative z depths of the 2 layers at points where they
70 * intersect when projected onto the 2d screen plane. Intersections are defined
71 * as corners that are positioned within the other quad, as well as
72 * intersections of the lines.
74 * We then choose the intersection point with the greatest difference in Z
75 * depths and use this point to determine an ordering for the two layers.
76 * For layers that are intersecting in 3d space, this essentially guesses an
77 * order. In a lot of cases we only intersect right at the edge point (3d cubes
78 * in particular) and this generates the 'correct' looking ordering. For planes
79 * that truely intersect, then there is no correct ordering and this remains
80 * unsolved without changing our rendering code.
82 static LayerSortOrder
CompareDepth(Layer
* aOne
, Layer
* aTwo
) {
84 ThebesRect(aOne
->GetLocalVisibleRegion().GetBounds().ToUnknownRect());
86 ThebesRect(aTwo
->GetLocalVisibleRegion().GetBounds().ToUnknownRect());
88 MOZ_ASSERT(aOne
->GetParent() && aOne
->GetParent()->Extend3DContext() &&
89 aTwo
->GetParent() && aTwo
->GetParent()->Extend3DContext());
90 // Effective transform of leaves may had been projected to 2D.
91 Matrix4x4 ourTransform
=
92 aOne
->GetLocalTransform() * aOne
->GetParent()->GetEffectiveTransform();
93 Matrix4x4 otherTransform
=
94 aTwo
->GetLocalTransform() * aTwo
->GetParent()->GetEffectiveTransform();
96 // Transform both rectangles and project into 2d space.
97 gfxQuad ourTransformedRect
= gfxUtils::TransformToQuad(ourRect
, ourTransform
);
98 gfxQuad otherTransformedRect
=
99 gfxUtils::TransformToQuad(otherRect
, otherTransform
);
101 gfxRect ourBounds
= ourTransformedRect
.GetBounds();
102 gfxRect otherBounds
= otherTransformedRect
.GetBounds();
104 if (!ourBounds
.Intersects(otherBounds
)) {
108 // Make a list of all points that are within the other rect.
109 // Could we just check Contains() on the bounds rects. ie, is it possible
110 // for layers to overlap without intersections (in 2d space) and yet still
111 // have their bounds rects not completely enclose each other?
112 nsTArray
<gfxPoint
> points
;
113 for (uint32_t i
= 0; i
< 4; i
++) {
114 if (ourTransformedRect
.Contains(otherTransformedRect
.mPoints
[i
])) {
115 points
.AppendElement(otherTransformedRect
.mPoints
[i
]);
117 if (otherTransformedRect
.Contains(ourTransformedRect
.mPoints
[i
])) {
118 points
.AppendElement(ourTransformedRect
.mPoints
[i
]);
122 // Look for intersections between lines (in 2d space) and use these as
123 // depth testing points.
124 for (uint32_t i
= 0; i
< 4; i
++) {
125 for (uint32_t j
= 0; j
< 4; j
++) {
126 gfxPoint intersection
;
127 gfxLineSegment
one(ourTransformedRect
.mPoints
[i
],
128 ourTransformedRect
.mPoints
[(i
+ 1) % 4]);
129 gfxLineSegment
two(otherTransformedRect
.mPoints
[j
],
130 otherTransformedRect
.mPoints
[(j
+ 1) % 4]);
131 if (one
.Intersects(two
, intersection
)) {
132 points
.AppendElement(intersection
);
137 // No intersections, no defined order between these layers.
138 if (points
.IsEmpty()) {
142 // Find the relative Z depths of each intersection point and check that the
143 // layers are in the same order.
144 gfxFloat highest
= 0;
145 for (uint32_t i
= 0; i
< points
.Length(); i
++) {
146 gfxFloat ourDepth
= RecoverZDepth(ourTransform
, points
.ElementAt(i
));
147 gfxFloat otherDepth
= RecoverZDepth(otherTransform
, points
.ElementAt(i
));
149 gfxFloat difference
= otherDepth
- ourDepth
;
151 if (fabs(difference
) > fabs(highest
)) {
152 highest
= difference
;
155 // If layers have the same depth keep the original order
156 if (fabs(highest
) < 0.1 || highest
>= 0) {
164 // #define USE_XTERM_COLORING
165 # ifdef USE_XTERM_COLORING
166 // List of color values, which can be added to the xterm foreground offset or
167 // background offset to generate a xterm color code.
168 // NOTE: The colors that we don't explicitly use (by name) are commented out,
169 // to avoid triggering Wunused-const-variable build warnings.
170 static const int XTERM_FOREGROUND_COLOR_OFFSET
= 30;
171 static const int XTERM_BACKGROUND_COLOR_OFFSET
= 40;
172 static const int BLACK
= 0;
173 // static const int RED = 1;
174 static const int GREEN
= 2;
175 // static const int YELLOW = 3;
176 // static const int BLUE = 4;
177 // static const int MAGENTA = 5;
178 // static const int CYAN = 6;
179 // static const int WHITE = 7;
181 static const int RESET
= 0;
182 // static const int BRIGHT = 1;
183 // static const int DIM = 2;
184 // static const int UNDERLINE = 3;
185 // static const int BLINK = 4;
186 // static const int REVERSE = 7;
187 // static const int HIDDEN = 8;
189 static void SetTextColor(uint32_t aColor
) {
192 /* Command is the control command to the terminal */
193 SprintfLiteral(command
, "%c[%d;%d;%dm", 0x1B, RESET
,
194 aColor
+ XTERM_FOREGROUND_COLOR_OFFSET
,
195 BLACK
+ XTERM_BACKGROUND_COLOR_OFFSET
);
196 printf("%s", command
);
199 static void print_layer_internal(FILE* aFile
, Layer
* aLayer
, uint32_t aColor
) {
200 SetTextColor(aColor
);
201 fprintf(aFile
, "%p", aLayer
);
206 const char* colors
[] = {"Black", "Red", "Green", "Yellow",
207 "Blue", "Magenta", "Cyan", "White"};
209 static void print_layer_internal(FILE* aFile
, Layer
* aLayer
, uint32_t aColor
) {
210 fprintf(aFile
, "%p(%s)", aLayer
, colors
[aColor
]);
214 static void print_layer(FILE* aFile
, Layer
* aLayer
) {
215 print_layer_internal(aFile
, aLayer
, aLayer
->GetDebugColorIndex());
218 static void DumpLayerList(nsTArray
<Layer
*>& aLayers
) {
219 for (uint32_t i
= 0; i
< aLayers
.Length(); i
++) {
220 print_layer(stderr
, aLayers
.ElementAt(i
));
221 fprintf(stderr
, " ");
223 fprintf(stderr
, "\n");
226 static void DumpEdgeList(DirectedGraph
<Layer
*>& aGraph
) {
227 const nsTArray
<DirectedGraph
<Layer
*>::Edge
>& edges
= aGraph
.GetEdgeList();
229 for (uint32_t i
= 0; i
< edges
.Length(); i
++) {
230 fprintf(stderr
, "From: ");
231 print_layer(stderr
, edges
.ElementAt(i
).mFrom
);
232 fprintf(stderr
, ", To: ");
233 print_layer(stderr
, edges
.ElementAt(i
).mTo
);
234 fprintf(stderr
, "\n");
239 // The maximum number of layers that we will attempt to sort. Anything
240 // greater than this will be left unsorted. We should consider enabling
241 // depth buffering for the scene in this case.
242 #define MAX_SORTABLE_LAYERS 100
244 uint32_t gColorIndex
= 1;
246 void SortLayersBy3DZOrder(nsTArray
<Layer
*>& aLayers
) {
247 uint32_t nodeCount
= aLayers
.Length();
248 if (nodeCount
> MAX_SORTABLE_LAYERS
) {
251 DirectedGraph
<Layer
*> graph
;
254 if (gfxEnv::DumpLayerSortList()) {
255 for (uint32_t i
= 0; i
< nodeCount
; i
++) {
256 if (aLayers
.ElementAt(i
)->GetDebugColorIndex() == 0) {
257 aLayers
.ElementAt(i
)->SetDebugColorIndex(gColorIndex
++);
258 if (gColorIndex
> 7) {
263 fprintf(stderr
, " --- Layers before sorting: --- \n");
264 DumpLayerList(aLayers
);
268 // Iterate layers and determine edges.
269 for (uint32_t i
= 0; i
< nodeCount
; i
++) {
270 for (uint32_t j
= i
+ 1; j
< nodeCount
; j
++) {
271 Layer
* a
= aLayers
.ElementAt(i
);
272 Layer
* b
= aLayers
.ElementAt(j
);
273 LayerSortOrder order
= CompareDepth(a
, b
);
274 if (order
== ABeforeB
) {
276 } else if (order
== BBeforeA
) {
283 if (gfxEnv::DumpLayerSortList()) {
284 fprintf(stderr
, " --- Edge List: --- \n");
289 // Build a new array using the graph.
290 nsTArray
<Layer
*> noIncoming
;
291 nsTArray
<Layer
*> sortedList
;
293 // Make a list of all layers with no incoming edges.
294 noIncoming
.AppendElements(aLayers
);
295 const nsTArray
<DirectedGraph
<Layer
*>::Edge
>& edges
= graph
.GetEdgeList();
296 for (uint32_t i
= 0; i
< edges
.Length(); i
++) {
297 noIncoming
.RemoveElement(edges
.ElementAt(i
).mTo
);
300 // Move each item without incoming edges into the sorted list,
301 // and remove edges from it.
303 if (!noIncoming
.IsEmpty()) {
304 uint32_t last
= noIncoming
.Length() - 1;
306 Layer
* layer
= noIncoming
.ElementAt(last
);
307 MOZ_ASSERT(layer
); // don't let null layer pointers sneak into sortedList
309 noIncoming
.RemoveElementAt(last
);
310 sortedList
.AppendElement(layer
);
312 nsTArray
<DirectedGraph
<Layer
*>::Edge
> outgoing
;
313 graph
.GetEdgesFrom(layer
, outgoing
);
314 for (uint32_t i
= 0; i
< outgoing
.Length(); i
++) {
315 DirectedGraph
<Layer
*>::Edge edge
= outgoing
.ElementAt(i
);
316 graph
.RemoveEdge(edge
);
317 if (!graph
.NumEdgesTo(edge
.mTo
)) {
318 // If this node also has no edges now, add it to the list
319 noIncoming
.AppendElement(edge
.mTo
);
324 // If there are no nodes without incoming edges, but there
325 // are still edges, then we have a cycle.
326 if (noIncoming
.IsEmpty() && graph
.GetEdgeCount()) {
327 // Find the node with the least incoming edges.
328 uint32_t minEdges
= UINT_MAX
;
329 Layer
* minNode
= nullptr;
330 for (uint32_t i
= 0; i
< aLayers
.Length(); i
++) {
331 uint32_t edgeCount
= graph
.NumEdgesTo(aLayers
.ElementAt(i
));
332 if (edgeCount
&& edgeCount
< minEdges
) {
333 minEdges
= edgeCount
;
334 minNode
= aLayers
.ElementAt(i
);
342 // Remove all of them!
343 graph
.RemoveEdgesTo(minNode
);
344 noIncoming
.AppendElement(minNode
);
347 } while (!noIncoming
.IsEmpty());
348 NS_ASSERTION(!graph
.GetEdgeCount(), "Cycles detected!");
350 if (gfxEnv::DumpLayerSortList()) {
351 fprintf(stderr
, " --- Layers after sorting: --- \n");
352 DumpLayerList(sortedList
);
357 aLayers
.AppendElements(sortedList
);
360 } // namespace layers
361 } // namespace mozilla