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 #include "api_exec_decl.h"
32
33 #include "state_tracker/st_cb_bitmap.h"
34 #include "state_tracker/st_context.h"
35
36 /**
37 * Set scissor rectangle data directly in ScissorArray
38 *
39 * This is an internal function that performs no error checking on the
40 * supplied data. It also does \b not call \c dd_function_table::Scissor.
41 *
42 * \sa _mesa_set_scissor
43 */
44 static void
set_scissor_no_notify(struct gl_context * ctx,unsigned idx,GLint x,GLint y,GLsizei width,GLsizei height)45 set_scissor_no_notify(struct gl_context *ctx, unsigned idx,
46 GLint x, GLint y, GLsizei width, GLsizei height)
47 {
48 if (x == ctx->Scissor.ScissorArray[idx].X &&
49 y == ctx->Scissor.ScissorArray[idx].Y &&
50 width == ctx->Scissor.ScissorArray[idx].Width &&
51 height == ctx->Scissor.ScissorArray[idx].Height)
52 return;
53
54 if (ctx->Scissor.EnableFlags)
55 st_flush_bitmap_cache(st_context(ctx));
56
57 FLUSH_VERTICES(ctx, 0, GL_SCISSOR_BIT);
58 ctx->NewDriverState |= ST_NEW_SCISSOR;
59
60 ctx->Scissor.ScissorArray[idx].X = x;
61 ctx->Scissor.ScissorArray[idx].Y = y;
62 ctx->Scissor.ScissorArray[idx].Width = width;
63 ctx->Scissor.ScissorArray[idx].Height = height;
64 }
65
66 static void
scissor(struct gl_context * ctx,GLint x,GLint y,GLsizei width,GLsizei height)67 scissor(struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height)
68 {
69 unsigned i;
70
71 /* The GL_ARB_viewport_array spec says:
72 *
73 * "Scissor sets the scissor rectangle for all viewports to the same
74 * values and is equivalent (assuming no errors are generated) to:
75 *
76 * for (uint i = 0; i < MAX_VIEWPORTS; i++) {
77 * ScissorIndexed(i, left, bottom, width, height);
78 * }"
79 *
80 * Set the scissor rectangle for all of the viewports supported by the
81 * implementation, but only signal the driver once at the end.
82 */
83 for (i = 0; i < ctx->Const.MaxViewports; i++)
84 set_scissor_no_notify(ctx, i, x, y, width, height);
85 }
86
87 /**
88 * Called via glScissor
89 */
90 void GLAPIENTRY
_mesa_Scissor_no_error(GLint x,GLint y,GLsizei width,GLsizei height)91 _mesa_Scissor_no_error(GLint x, GLint y, GLsizei width, GLsizei height)
92 {
93 GET_CURRENT_CONTEXT(ctx);
94 scissor(ctx, x, y, width, height);
95 }
96
97 void GLAPIENTRY
_mesa_Scissor(GLint x,GLint y,GLsizei width,GLsizei height)98 _mesa_Scissor(GLint x, GLint y, GLsizei width, GLsizei height)
99 {
100 GET_CURRENT_CONTEXT(ctx);
101
102 if (MESA_VERBOSE & VERBOSE_API)
103 _mesa_debug(ctx, "glScissor %d %d %d %d\n", x, y, width, height);
104
105 if (width < 0 || height < 0) {
106 _mesa_error( ctx, GL_INVALID_VALUE, "glScissor" );
107 return;
108 }
109
110 scissor(ctx, x, y, width, height);
111 }
112
113
114 /**
115 * Define the scissor box.
116 *
117 * \param x, y coordinates of the scissor box lower-left corner.
118 * \param width width of the scissor box.
119 * \param height height of the scissor box.
120 *
121 * \sa glScissor().
122 *
123 * Verifies the parameters and updates __struct gl_contextRec::Scissor. On a
124 * change flushes the vertices and notifies the driver via
125 * the dd_function_table::Scissor callback.
126 */
127 void
_mesa_set_scissor(struct gl_context * ctx,unsigned idx,GLint x,GLint y,GLsizei width,GLsizei height)128 _mesa_set_scissor(struct gl_context *ctx, unsigned idx,
129 GLint x, GLint y, GLsizei width, GLsizei height)
130 {
131 set_scissor_no_notify(ctx, idx, x, y, width, height);
132 }
133
134 static void
scissor_array(struct gl_context * ctx,GLuint first,GLsizei count,struct gl_scissor_rect * rect)135 scissor_array(struct gl_context *ctx, GLuint first, GLsizei count,
136 struct gl_scissor_rect *rect)
137 {
138 for (GLsizei i = 0; i < count; i++) {
139 set_scissor_no_notify(ctx, i + first, rect[i].X, rect[i].Y,
140 rect[i].Width, rect[i].Height);
141 }
142 }
143
144 /**
145 * Define count scissor boxes starting at index.
146 *
147 * \param index index of first scissor records to set
148 * \param count number of scissor records to set
149 * \param x, y pointer to array of struct gl_scissor_rects
150 *
151 * \sa glScissorArrayv().
152 *
153 * Verifies the parameters and call set_scissor_no_notify to do the work.
154 */
155 void GLAPIENTRY
_mesa_ScissorArrayv_no_error(GLuint first,GLsizei count,const GLint * v)156 _mesa_ScissorArrayv_no_error(GLuint first, GLsizei count, const GLint *v)
157 {
158 GET_CURRENT_CONTEXT(ctx);
159
160 struct gl_scissor_rect *p = (struct gl_scissor_rect *)v;
161 scissor_array(ctx, first, count, p);
162 }
163
164 void GLAPIENTRY
_mesa_ScissorArrayv(GLuint first,GLsizei count,const GLint * v)165 _mesa_ScissorArrayv(GLuint first, GLsizei count, const GLint *v)
166 {
167 int i;
168 struct gl_scissor_rect *p = (struct gl_scissor_rect *) v;
169 GET_CURRENT_CONTEXT(ctx);
170
171 if ((first + count) > ctx->Const.MaxViewports) {
172 _mesa_error(ctx, GL_INVALID_VALUE,
173 "glScissorArrayv: first (%d) + count (%d) >= MaxViewports (%d)",
174 first, count, ctx->Const.MaxViewports);
175 return;
176 }
177
178 /* Verify width & height */
179 for (i = 0; i < count; i++) {
180 if (p[i].Width < 0 || p[i].Height < 0) {
181 _mesa_error(ctx, GL_INVALID_VALUE,
182 "glScissorArrayv: index (%d) width or height < 0 (%d, %d)",
183 i, p[i].Width, p[i].Height);
184 return;
185 }
186 }
187
188 scissor_array(ctx, first, count, p);
189 }
190
191 /**
192 * Define the scissor box.
193 *
194 * \param index index of scissor records to set
195 * \param x, y coordinates of the scissor box lower-left corner.
196 * \param width width of the scissor box.
197 * \param height height of the scissor box.
198 *
199 * Verifies the parameters call set_scissor_no_notify to do the work.
200 */
201 static void
scissor_indexed_err(struct gl_context * ctx,GLuint index,GLint left,GLint bottom,GLsizei width,GLsizei height,const char * function)202 scissor_indexed_err(struct gl_context *ctx, GLuint index, GLint left,
203 GLint bottom, GLsizei width, GLsizei height,
204 const char *function)
205 {
206 if (MESA_VERBOSE & VERBOSE_API)
207 _mesa_debug(ctx, "%s(%d, %d, %d, %d, %d)\n",
208 function, index, left, bottom, width, height);
209
210 if (index >= ctx->Const.MaxViewports) {
211 _mesa_error(ctx, GL_INVALID_VALUE,
212 "%s: index (%d) >= MaxViewports (%d)",
213 function, index, ctx->Const.MaxViewports);
214 return;
215 }
216
217 if (width < 0 || height < 0) {
218 _mesa_error(ctx, GL_INVALID_VALUE,
219 "%s: index (%d) width or height < 0 (%d, %d)",
220 function, index, width, height);
221 return;
222 }
223
224 _mesa_set_scissor(ctx, index, left, bottom, width, height);
225 }
226
227 void GLAPIENTRY
_mesa_ScissorIndexed_no_error(GLuint index,GLint left,GLint bottom,GLsizei width,GLsizei height)228 _mesa_ScissorIndexed_no_error(GLuint index, GLint left, GLint bottom,
229 GLsizei width, GLsizei height)
230 {
231 GET_CURRENT_CONTEXT(ctx);
232 _mesa_set_scissor(ctx, index, left, bottom, width, height);
233 }
234
235 void GLAPIENTRY
_mesa_ScissorIndexed(GLuint index,GLint left,GLint bottom,GLsizei width,GLsizei height)236 _mesa_ScissorIndexed(GLuint index, GLint left, GLint bottom,
237 GLsizei width, GLsizei height)
238 {
239 GET_CURRENT_CONTEXT(ctx);
240 scissor_indexed_err(ctx, index, left, bottom, width, height,
241 "glScissorIndexed");
242 }
243
244 void GLAPIENTRY
_mesa_ScissorIndexedv_no_error(GLuint index,const GLint * v)245 _mesa_ScissorIndexedv_no_error(GLuint index, const GLint *v)
246 {
247 GET_CURRENT_CONTEXT(ctx);
248 _mesa_set_scissor(ctx, index, v[0], v[1], v[2], v[3]);
249 }
250
251 void GLAPIENTRY
_mesa_ScissorIndexedv(GLuint index,const GLint * v)252 _mesa_ScissorIndexedv(GLuint index, const GLint *v)
253 {
254 GET_CURRENT_CONTEXT(ctx);
255 scissor_indexed_err(ctx, index, v[0], v[1], v[2], v[3],
256 "glScissorIndexedv");
257 }
258
259 void GLAPIENTRY
_mesa_WindowRectanglesEXT(GLenum mode,GLsizei count,const GLint * box)260 _mesa_WindowRectanglesEXT(GLenum mode, GLsizei count, const GLint *box)
261 {
262 int i;
263 struct gl_scissor_rect newval[MAX_WINDOW_RECTANGLES];
264 GET_CURRENT_CONTEXT(ctx);
265
266 if (MESA_VERBOSE & VERBOSE_API)
267 _mesa_debug(ctx, "glWindowRectanglesEXT(%s, %d, %p)\n",
268 _mesa_enum_to_string(mode), count, box);
269
270 if (mode != GL_INCLUSIVE_EXT && mode != GL_EXCLUSIVE_EXT) {
271 _mesa_error(ctx, GL_INVALID_ENUM,
272 "glWindowRectanglesEXT(invalid mode 0x%x)", mode);
273 return;
274 }
275
276 if (count < 0) {
277 _mesa_error(ctx, GL_INVALID_VALUE, "glWindowRectanglesEXT(count < 0)");
278 return;
279 }
280
281 if (count > ctx->Const.MaxWindowRectangles) {
282 _mesa_error(ctx, GL_INVALID_VALUE,
283 "glWindowRectanglesEXT(count >= MaxWindowRectangles (%d)",
284 ctx->Const.MaxWindowRectangles);
285 return;
286 }
287
288 for (i = 0; i < count; i++) {
289 if (box[2] < 0 || box[3] < 0) {
290 _mesa_error(ctx, GL_INVALID_VALUE,
291 "glWindowRectanglesEXT(box %d: w < 0 || h < 0)", i);
292 return;
293 }
294 newval[i].X = box[0];
295 newval[i].Y = box[1];
296 newval[i].Width = box[2];
297 newval[i].Height = box[3];
298 box += 4;
299 }
300
301 st_flush_bitmap_cache(st_context(ctx));
302
303 FLUSH_VERTICES(ctx, 0, GL_SCISSOR_BIT);
304 ctx->NewDriverState |= ST_NEW_WINDOW_RECTANGLES;
305
306 memcpy(ctx->Scissor.WindowRects, newval,
307 sizeof(struct gl_scissor_rect) * count);
308 ctx->Scissor.NumWindowRects = count;
309 ctx->Scissor.WindowRectMode = mode;
310 }
311
312
313 /**
314 * Initialize the context's scissor state.
315 * \param ctx the GL context.
316 */
317 void
_mesa_init_scissor(struct gl_context * ctx)318 _mesa_init_scissor(struct gl_context *ctx)
319 {
320 unsigned i;
321
322 /* Scissor group */
323 ctx->Scissor.EnableFlags = 0;
324 ctx->Scissor.WindowRectMode = GL_EXCLUSIVE_EXT;
325
326 /* Note: ctx->Const.MaxViewports may not have been set by the driver yet,
327 * so just initialize all of them.
328 */
329 for (i = 0; i < MAX_VIEWPORTS; i++)
330 set_scissor_no_notify(ctx, i, 0, 0, 0, 0);
331 }
332