• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.scenes.scene2d.utils;
18 
19 import com.badlogic.gdx.Gdx;
20 import com.badlogic.gdx.graphics.Camera;
21 import com.badlogic.gdx.graphics.GL20;
22 import com.badlogic.gdx.graphics.glutils.HdpiUtils;
23 import com.badlogic.gdx.math.Matrix4;
24 import com.badlogic.gdx.math.Rectangle;
25 import com.badlogic.gdx.math.Vector3;
26 import com.badlogic.gdx.utils.Array;
27 
28 /** A stack of {@link Rectangle} objects to be used for clipping via {@link GL20#glScissor(int, int, int, int)}. When a new
29  * Rectangle is pushed onto the stack, it will be merged with the current top of stack. The minimum area of overlap is then set as
30  * the real top of the stack.
31  * @author mzechner */
32 public class ScissorStack {
33 	private static Array<Rectangle> scissors = new Array<Rectangle>();
34 	static Vector3 tmp = new Vector3();
35 	static final Rectangle viewport = new Rectangle();
36 
37 	/** Pushes a new scissor {@link Rectangle} onto the stack, merging it with the current top of the stack. The minimal area of
38 	 * overlap between the top of stack rectangle and the provided rectangle is pushed onto the stack. This will invoke
39 	 * {@link GL20#glScissor(int, int, int, int)} with the final top of stack rectangle. In case no scissor is yet on the stack
40 	 * this will also enable {@link GL20#GL_SCISSOR_TEST} automatically.
41 	 * <p>
42 	 * Any drawing should be flushed before pushing scissors.
43 	 * @return true if the scissors were pushed. false if the scissor area was zero, in this case the scissors were not pushed and
44 	 *         no drawing should occur. */
pushScissors(Rectangle scissor)45 	public static boolean pushScissors (Rectangle scissor) {
46 		fix(scissor);
47 
48 		if (scissors.size == 0) {
49 			if (scissor.width < 1 || scissor.height < 1) return false;
50 			Gdx.gl.glEnable(GL20.GL_SCISSOR_TEST);
51 		} else {
52 			// merge scissors
53 			Rectangle parent = scissors.get(scissors.size - 1);
54 			float minX = Math.max(parent.x, scissor.x);
55 			float maxX = Math.min(parent.x + parent.width, scissor.x + scissor.width);
56 			if (maxX - minX < 1) return false;
57 
58 			float minY = Math.max(parent.y, scissor.y);
59 			float maxY = Math.min(parent.y + parent.height, scissor.y + scissor.height);
60 			if (maxY - minY < 1) return false;
61 
62 			scissor.x = minX;
63 			scissor.y = minY;
64 			scissor.width = maxX - minX;
65 			scissor.height = Math.max(1, maxY - minY);
66 		}
67 		scissors.add(scissor);
68 		HdpiUtils.glScissor((int)scissor.x, (int)scissor.y, (int)scissor.width, (int)scissor.height);
69 		return true;
70 	}
71 
72 	/** Pops the current scissor rectangle from the stack and sets the new scissor area to the new top of stack rectangle. In case
73 	 * no more rectangles are on the stack, {@link GL20#GL_SCISSOR_TEST} is disabled.
74 	 * <p>
75 	 * Any drawing should be flushed before popping scissors. */
popScissors()76 	public static Rectangle popScissors () {
77 		Rectangle old = scissors.pop();
78 		if (scissors.size == 0)
79 			Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST);
80 		else {
81 			Rectangle scissor = scissors.peek();
82 			HdpiUtils.glScissor((int)scissor.x, (int)scissor.y, (int)scissor.width, (int)scissor.height);
83 		}
84 		return old;
85 	}
86 
peekScissors()87 	public static Rectangle peekScissors () {
88 		return scissors.peek();
89 	}
90 
fix(Rectangle rect)91 	private static void fix (Rectangle rect) {
92 		rect.x = Math.round(rect.x);
93 		rect.y = Math.round(rect.y);
94 		rect.width = Math.round(rect.width);
95 		rect.height = Math.round(rect.height);
96 		if (rect.width < 0) {
97 			rect.width = -rect.width;
98 			rect.x -= rect.width;
99 		}
100 		if (rect.height < 0) {
101 			rect.height = -rect.height;
102 			rect.y -= rect.height;
103 		}
104 	}
105 
106 	/** Calculates a scissor rectangle using 0,0,Gdx.graphics.getWidth(),Gdx.graphics.getHeight() as the viewport.
107 	 * @see #calculateScissors(Camera, float, float, float, float, Matrix4, Rectangle, Rectangle) */
calculateScissors(Camera camera, Matrix4 batchTransform, Rectangle area, Rectangle scissor)108 	public static void calculateScissors (Camera camera, Matrix4 batchTransform, Rectangle area, Rectangle scissor) {
109 		calculateScissors(camera, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), batchTransform, area, scissor);
110 	}
111 
112 	/** Calculates a scissor rectangle in OpenGL ES window coordinates from a {@link Camera}, a transformation {@link Matrix4} and
113 	 * an axis aligned {@link Rectangle}. The rectangle will get transformed by the camera and transform matrices and is then
114 	 * projected to screen coordinates. Note that only axis aligned rectangles will work with this method. If either the Camera or
115 	 * the Matrix4 have rotational components, the output of this method will not be suitable for
116 	 * {@link GL20#glScissor(int, int, int, int)}.
117 	 * @param camera the {@link Camera}
118 	 * @param batchTransform the transformation {@link Matrix4}
119 	 * @param area the {@link Rectangle} to transform to window coordinates
120 	 * @param scissor the Rectangle to store the result in */
calculateScissors(Camera camera, float viewportX, float viewportY, float viewportWidth, float viewportHeight, Matrix4 batchTransform, Rectangle area, Rectangle scissor)121 	public static void calculateScissors (Camera camera, float viewportX, float viewportY, float viewportWidth,
122 		float viewportHeight, Matrix4 batchTransform, Rectangle area, Rectangle scissor) {
123 		tmp.set(area.x, area.y, 0);
124 		tmp.mul(batchTransform);
125 		camera.project(tmp, viewportX, viewportY, viewportWidth, viewportHeight);
126 		scissor.x = tmp.x;
127 		scissor.y = tmp.y;
128 
129 		tmp.set(area.x + area.width, area.y + area.height, 0);
130 		tmp.mul(batchTransform);
131 		camera.project(tmp, viewportX, viewportY, viewportWidth, viewportHeight);
132 		scissor.width = tmp.x - scissor.x;
133 		scissor.height = tmp.y - scissor.y;
134 	}
135 
136 	/** @return the current viewport in OpenGL ES window coordinates based on the currently applied scissor */
getViewport()137 	public static Rectangle getViewport () {
138 		if (scissors.size == 0) {
139 			viewport.set(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
140 			return viewport;
141 		} else {
142 			Rectangle scissor = scissors.peek();
143 			viewport.set(scissor);
144 			return viewport;
145 		}
146 	}
147 }
148