xref: /third_party/mesa3d/src/mesa/main/scissor.c (revision bf215546)
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 */
44static void
45set_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
66static void
67scissor(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 */
90void GLAPIENTRY
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
97void GLAPIENTRY
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 */
127void
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
134static void
135scissor_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 */
155void GLAPIENTRY
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
164void GLAPIENTRY
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 */
201static void
202scissor_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
227void GLAPIENTRY
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
235void GLAPIENTRY
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
244void GLAPIENTRY
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
251void GLAPIENTRY
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
259void GLAPIENTRY
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 */
317void
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