1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 #include "main/glheader.h"
27 #include "main/context.h"
28 #include "main/enums.h"
29 #include "main/mtypes.h"
30 #include "main/scissor.h"
31
32
33 /**
34 * Set scissor rectangle data directly in ScissorArray
35 *
36 * This is an internal function that performs no error checking on the
37 * supplied data. It also does \b not call \c dd_function_table::Scissor.
38 *
39 * \sa _mesa_set_scissor
40 */
41 static void
set_scissor_no_notify(struct gl_context * ctx,unsigned idx,GLint x,GLint y,GLsizei width,GLsizei height)42 set_scissor_no_notify(struct gl_context *ctx, unsigned idx,
43 GLint x, GLint y, GLsizei width, GLsizei height)
44 {
45 if (x == ctx->Scissor.ScissorArray[idx].X &&
46 y == ctx->Scissor.ScissorArray[idx].Y &&
47 width == ctx->Scissor.ScissorArray[idx].Width &&
48 height == ctx->Scissor.ScissorArray[idx].Height)
49 return;
50
51 FLUSH_VERTICES(ctx, ctx->DriverFlags.NewScissorRect ? 0 : _NEW_SCISSOR,
52 GL_SCISSOR_BIT);
53 ctx->NewDriverState |= ctx->DriverFlags.NewScissorRect;
54
55 ctx->Scissor.ScissorArray[idx].X = x;
56 ctx->Scissor.ScissorArray[idx].Y = y;
57 ctx->Scissor.ScissorArray[idx].Width = width;
58 ctx->Scissor.ScissorArray[idx].Height = height;
59 }
60
61 static void
scissor(struct gl_context * ctx,GLint x,GLint y,GLsizei width,GLsizei height)62 scissor(struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height)
63 {
64 unsigned i;
65
66 /* The GL_ARB_viewport_array spec says:
67 *
68 * "Scissor sets the scissor rectangle for all viewports to the same
69 * values and is equivalent (assuming no errors are generated) to:
70 *
71 * for (uint i = 0; i < MAX_VIEWPORTS; i++) {
72 * ScissorIndexed(i, left, bottom, width, height);
73 * }"
74 *
75 * Set the scissor rectangle for all of the viewports supported by the
76 * implementation, but only signal the driver once at the end.
77 */
78 for (i = 0; i < ctx->Const.MaxViewports; i++)
79 set_scissor_no_notify(ctx, i, x, y, width, height);
80
81 if (ctx->Driver.Scissor)
82 ctx->Driver.Scissor(ctx);
83 }
84
85 /**
86 * Called via glScissor
87 */
88 void GLAPIENTRY
_mesa_Scissor_no_error(GLint x,GLint y,GLsizei width,GLsizei height)89 _mesa_Scissor_no_error(GLint x, GLint y, GLsizei width, GLsizei height)
90 {
91 GET_CURRENT_CONTEXT(ctx);
92 scissor(ctx, x, y, width, height);
93 }
94
95 void GLAPIENTRY
_mesa_Scissor(GLint x,GLint y,GLsizei width,GLsizei height)96 _mesa_Scissor(GLint x, GLint y, GLsizei width, GLsizei height)
97 {
98 GET_CURRENT_CONTEXT(ctx);
99
100 if (MESA_VERBOSE & VERBOSE_API)
101 _mesa_debug(ctx, "glScissor %d %d %d %d\n", x, y, width, height);
102
103 if (width < 0 || height < 0) {
104 _mesa_error( ctx, GL_INVALID_VALUE, "glScissor" );
105 return;
106 }
107
108 scissor(ctx, x, y, width, height);
109 }
110
111
112 /**
113 * Define the scissor box.
114 *
115 * \param x, y coordinates of the scissor box lower-left corner.
116 * \param width width of the scissor box.
117 * \param height height of the scissor box.
118 *
119 * \sa glScissor().
120 *
121 * Verifies the parameters and updates __struct gl_contextRec::Scissor. On a
122 * change flushes the vertices and notifies the driver via
123 * the dd_function_table::Scissor callback.
124 */
125 void
_mesa_set_scissor(struct gl_context * ctx,unsigned idx,GLint x,GLint y,GLsizei width,GLsizei height)126 _mesa_set_scissor(struct gl_context *ctx, unsigned idx,
127 GLint x, GLint y, GLsizei width, GLsizei height)
128 {
129 set_scissor_no_notify(ctx, idx, x, y, width, height);
130
131 if (ctx->Driver.Scissor)
132 ctx->Driver.Scissor(ctx);
133 }
134
135 static void
scissor_array(struct gl_context * ctx,GLuint first,GLsizei count,struct gl_scissor_rect * rect)136 scissor_array(struct gl_context *ctx, GLuint first, GLsizei count,
137 struct gl_scissor_rect *rect)
138 {
139 for (GLsizei i = 0; i < count; i++) {
140 set_scissor_no_notify(ctx, i + first, rect[i].X, rect[i].Y,
141 rect[i].Width, rect[i].Height);
142 }
143
144 if (ctx->Driver.Scissor)
145 ctx->Driver.Scissor(ctx);
146 }
147
148 /**
149 * Define count scissor boxes starting at index.
150 *
151 * \param index index of first scissor records to set
152 * \param count number of scissor records to set
153 * \param x, y pointer to array of struct gl_scissor_rects
154 *
155 * \sa glScissorArrayv().
156 *
157 * Verifies the parameters and call set_scissor_no_notify to do the work.
158 */
159 void GLAPIENTRY
_mesa_ScissorArrayv_no_error(GLuint first,GLsizei count,const GLint * v)160 _mesa_ScissorArrayv_no_error(GLuint first, GLsizei count, const GLint *v)
161 {
162 GET_CURRENT_CONTEXT(ctx);
163
164 struct gl_scissor_rect *p = (struct gl_scissor_rect *)v;
165 scissor_array(ctx, first, count, p);
166 }
167
168 void GLAPIENTRY
_mesa_ScissorArrayv(GLuint first,GLsizei count,const GLint * v)169 _mesa_ScissorArrayv(GLuint first, GLsizei count, const GLint *v)
170 {
171 int i;
172 struct gl_scissor_rect *p = (struct gl_scissor_rect *) v;
173 GET_CURRENT_CONTEXT(ctx);
174
175 if ((first + count) > ctx->Const.MaxViewports) {
176 _mesa_error(ctx, GL_INVALID_VALUE,
177 "glScissorArrayv: first (%d) + count (%d) >= MaxViewports (%d)",
178 first, count, ctx->Const.MaxViewports);
179 return;
180 }
181
182 /* Verify width & height */
183 for (i = 0; i < count; i++) {
184 if (p[i].Width < 0 || p[i].Height < 0) {
185 _mesa_error(ctx, GL_INVALID_VALUE,
186 "glScissorArrayv: index (%d) width or height < 0 (%d, %d)",
187 i, p[i].Width, p[i].Height);
188 return;
189 }
190 }
191
192 scissor_array(ctx, first, count, p);
193 }
194
195 /**
196 * Define the scissor box.
197 *
198 * \param index index of scissor records to set
199 * \param x, y coordinates of the scissor box lower-left corner.
200 * \param width width of the scissor box.
201 * \param height height of the scissor box.
202 *
203 * Verifies the parameters call set_scissor_no_notify to do the work.
204 */
205 static void
scissor_indexed_err(struct gl_context * ctx,GLuint index,GLint left,GLint bottom,GLsizei width,GLsizei height,const char * function)206 scissor_indexed_err(struct gl_context *ctx, GLuint index, GLint left,
207 GLint bottom, GLsizei width, GLsizei height,
208 const char *function)
209 {
210 if (MESA_VERBOSE & VERBOSE_API)
211 _mesa_debug(ctx, "%s(%d, %d, %d, %d, %d)\n",
212 function, index, left, bottom, width, height);
213
214 if (index >= ctx->Const.MaxViewports) {
215 _mesa_error(ctx, GL_INVALID_VALUE,
216 "%s: index (%d) >= MaxViewports (%d)",
217 function, index, ctx->Const.MaxViewports);
218 return;
219 }
220
221 if (width < 0 || height < 0) {
222 _mesa_error(ctx, GL_INVALID_VALUE,
223 "%s: index (%d) width or height < 0 (%d, %d)",
224 function, index, width, height);
225 return;
226 }
227
228 _mesa_set_scissor(ctx, index, left, bottom, width, height);
229 }
230
231 void GLAPIENTRY
_mesa_ScissorIndexed_no_error(GLuint index,GLint left,GLint bottom,GLsizei width,GLsizei height)232 _mesa_ScissorIndexed_no_error(GLuint index, GLint left, GLint bottom,
233 GLsizei width, GLsizei height)
234 {
235 GET_CURRENT_CONTEXT(ctx);
236 _mesa_set_scissor(ctx, index, left, bottom, width, height);
237 }
238
239 void GLAPIENTRY
_mesa_ScissorIndexed(GLuint index,GLint left,GLint bottom,GLsizei width,GLsizei height)240 _mesa_ScissorIndexed(GLuint index, GLint left, GLint bottom,
241 GLsizei width, GLsizei height)
242 {
243 GET_CURRENT_CONTEXT(ctx);
244 scissor_indexed_err(ctx, index, left, bottom, width, height,
245 "glScissorIndexed");
246 }
247
248 void GLAPIENTRY
_mesa_ScissorIndexedv_no_error(GLuint index,const GLint * v)249 _mesa_ScissorIndexedv_no_error(GLuint index, const GLint *v)
250 {
251 GET_CURRENT_CONTEXT(ctx);
252 _mesa_set_scissor(ctx, index, v[0], v[1], v[2], v[3]);
253 }
254
255 void GLAPIENTRY
_mesa_ScissorIndexedv(GLuint index,const GLint * v)256 _mesa_ScissorIndexedv(GLuint index, const GLint *v)
257 {
258 GET_CURRENT_CONTEXT(ctx);
259 scissor_indexed_err(ctx, index, v[0], v[1], v[2], v[3],
260 "glScissorIndexedv");
261 }
262
263 void GLAPIENTRY
_mesa_WindowRectanglesEXT(GLenum mode,GLsizei count,const GLint * box)264 _mesa_WindowRectanglesEXT(GLenum mode, GLsizei count, const GLint *box)
265 {
266 int i;
267 struct gl_scissor_rect newval[MAX_WINDOW_RECTANGLES];
268 GET_CURRENT_CONTEXT(ctx);
269
270 if (MESA_VERBOSE & VERBOSE_API)
271 _mesa_debug(ctx, "glWindowRectanglesEXT(%s, %d, %p)\n",
272 _mesa_enum_to_string(mode), count, box);
273
274 if (mode != GL_INCLUSIVE_EXT && mode != GL_EXCLUSIVE_EXT) {
275 _mesa_error(ctx, GL_INVALID_ENUM,
276 "glWindowRectanglesEXT(invalid mode 0x%x)", mode);
277 return;
278 }
279
280 if (count < 0) {
281 _mesa_error(ctx, GL_INVALID_VALUE, "glWindowRectanglesEXT(count < 0)");
282 return;
283 }
284
285 if (count > ctx->Const.MaxWindowRectangles) {
286 _mesa_error(ctx, GL_INVALID_VALUE,
287 "glWindowRectanglesEXT(count >= MaxWindowRectangles (%d)",
288 ctx->Const.MaxWindowRectangles);
289 return;
290 }
291
292 for (i = 0; i < count; i++) {
293 if (box[2] < 0 || box[3] < 0) {
294 _mesa_error(ctx, GL_INVALID_VALUE,
295 "glWindowRectanglesEXT(box %d: w < 0 || h < 0)", i);
296 return;
297 }
298 newval[i].X = box[0];
299 newval[i].Y = box[1];
300 newval[i].Width = box[2];
301 newval[i].Height = box[3];
302 box += 4;
303 }
304
305 FLUSH_VERTICES(ctx, 0, GL_SCISSOR_BIT);
306 ctx->NewDriverState |= ctx->DriverFlags.NewWindowRectangles;
307
308 memcpy(ctx->Scissor.WindowRects, newval,
309 sizeof(struct gl_scissor_rect) * count);
310 ctx->Scissor.NumWindowRects = count;
311 ctx->Scissor.WindowRectMode = mode;
312 }
313
314
315 /**
316 * Initialize the context's scissor state.
317 * \param ctx the GL context.
318 */
319 void
_mesa_init_scissor(struct gl_context * ctx)320 _mesa_init_scissor(struct gl_context *ctx)
321 {
322 unsigned i;
323
324 /* Scissor group */
325 ctx->Scissor.EnableFlags = 0;
326 ctx->Scissor.WindowRectMode = GL_EXCLUSIVE_EXT;
327
328 /* Note: ctx->Const.MaxViewports may not have been set by the driver yet,
329 * so just initialize all of them.
330 */
331 for (i = 0; i < MAX_VIEWPORTS; i++)
332 set_scissor_no_notify(ctx, i, 0, 0, 0, 0);
333 }
334