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