1/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2008  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 * \file pixelstore.c
27 * glPixelStore functions.
28 */
29
30
31#include "glheader.h"
32#include "bufferobj.h"
33#include "context.h"
34#include "pixelstore.h"
35#include "mtypes.h"
36#include "util/rounding.h"
37
38
39static ALWAYS_INLINE void
40pixel_storei(GLenum pname, GLint param, bool no_error)
41{
42   /* NOTE: this call can't be compiled into the display list */
43   GET_CURRENT_CONTEXT(ctx);
44
45   switch (pname) {
46      case GL_PACK_SWAP_BYTES:
47         if (!no_error && !_mesa_is_desktop_gl(ctx))
48            goto invalid_enum_error;
49         ctx->Pack.SwapBytes = param ? GL_TRUE : GL_FALSE;
50         break;
51      case GL_PACK_LSB_FIRST:
52         if (!no_error && !_mesa_is_desktop_gl(ctx))
53            goto invalid_enum_error;
54         ctx->Pack.LsbFirst = param ? GL_TRUE : GL_FALSE;
55         break;
56      case GL_PACK_ROW_LENGTH:
57         if (!no_error && !_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx))
58            goto invalid_enum_error;
59         if (!no_error && param<0)
60            goto invalid_value_error;
61         ctx->Pack.RowLength = param;
62         break;
63      case GL_PACK_IMAGE_HEIGHT:
64         if (!no_error && !_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx))
65            goto invalid_enum_error;
66         if (!no_error && param<0)
67            goto invalid_value_error;
68         ctx->Pack.ImageHeight = param;
69         break;
70      case GL_PACK_SKIP_PIXELS:
71         if (!no_error && !_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx))
72            goto invalid_enum_error;
73         if (!no_error && param<0)
74            goto invalid_value_error;
75         ctx->Pack.SkipPixels = param;
76         break;
77      case GL_PACK_SKIP_ROWS:
78         if (!no_error && !_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx))
79            goto invalid_enum_error;
80         if (!no_error && param<0)
81            goto invalid_value_error;
82         ctx->Pack.SkipRows = param;
83         break;
84      case GL_PACK_SKIP_IMAGES:
85         if (!no_error && !_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx))
86            goto invalid_enum_error;
87         if (!no_error && param<0)
88            goto invalid_value_error;
89         ctx->Pack.SkipImages = param;
90         break;
91      case GL_PACK_ALIGNMENT:
92         if (!no_error && param!=1 && param!=2 && param!=4 && param!=8)
93            goto invalid_value_error;
94         ctx->Pack.Alignment = param;
95         break;
96      case GL_PACK_INVERT_MESA:
97         if (!no_error && !_mesa_has_MESA_pack_invert(ctx))
98            goto invalid_enum_error;
99         ctx->Pack.Invert = param;
100         break;
101      case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
102         if (!no_error && !_mesa_has_ANGLE_pack_reverse_row_order(ctx))
103            goto invalid_enum_error;
104         ctx->Pack.Invert = param;
105         break;
106      case GL_PACK_COMPRESSED_BLOCK_WIDTH:
107         if (!no_error && !_mesa_is_desktop_gl(ctx))
108            goto invalid_enum_error;
109         if (!no_error && param<0)
110            goto invalid_value_error;
111         ctx->Pack.CompressedBlockWidth = param;
112         break;
113      case GL_PACK_COMPRESSED_BLOCK_HEIGHT:
114         if (!no_error && !_mesa_is_desktop_gl(ctx))
115            goto invalid_enum_error;
116         if (!no_error && param<0)
117            goto invalid_value_error;
118         ctx->Pack.CompressedBlockHeight = param;
119         break;
120      case GL_PACK_COMPRESSED_BLOCK_DEPTH:
121         if (!no_error && !_mesa_is_desktop_gl(ctx))
122            goto invalid_enum_error;
123         if (!no_error && param<0)
124            goto invalid_value_error;
125         ctx->Pack.CompressedBlockDepth = param;
126         break;
127      case GL_PACK_COMPRESSED_BLOCK_SIZE:
128         if (!no_error && !_mesa_is_desktop_gl(ctx))
129            goto invalid_enum_error;
130         if (!no_error && param<0)
131            goto invalid_value_error;
132         ctx->Pack.CompressedBlockSize = param;
133         break;
134
135      case GL_UNPACK_SWAP_BYTES:
136         if (!no_error && !_mesa_is_desktop_gl(ctx))
137            goto invalid_enum_error;
138         ctx->Unpack.SwapBytes = param ? GL_TRUE : GL_FALSE;
139         break;
140      case GL_UNPACK_LSB_FIRST:
141         if (!no_error && !_mesa_is_desktop_gl(ctx))
142            goto invalid_enum_error;
143         ctx->Unpack.LsbFirst = param ? GL_TRUE : GL_FALSE;
144         break;
145      case GL_UNPACK_ROW_LENGTH:
146         if (!no_error && ctx->API == API_OPENGLES)
147            goto invalid_enum_error;
148         if (!no_error && param<0)
149            goto invalid_value_error;
150         ctx->Unpack.RowLength = param;
151         break;
152      case GL_UNPACK_IMAGE_HEIGHT:
153         if (!no_error && !_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx))
154            goto invalid_enum_error;
155         if (!no_error && param<0)
156            goto invalid_value_error;
157         ctx->Unpack.ImageHeight = param;
158         break;
159      case GL_UNPACK_SKIP_PIXELS:
160         if (!no_error && ctx->API == API_OPENGLES)
161            goto invalid_enum_error;
162         if (!no_error && param<0)
163            goto invalid_value_error;
164         ctx->Unpack.SkipPixels = param;
165         break;
166      case GL_UNPACK_SKIP_ROWS:
167         if (!no_error && ctx->API == API_OPENGLES)
168            goto invalid_enum_error;
169         if (!no_error && param<0)
170            goto invalid_value_error;
171         ctx->Unpack.SkipRows = param;
172         break;
173      case GL_UNPACK_SKIP_IMAGES:
174         if (!no_error && !_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx))
175            goto invalid_enum_error;
176         if (!no_error && param < 0)
177            goto invalid_value_error;
178         ctx->Unpack.SkipImages = param;
179         break;
180      case GL_UNPACK_ALIGNMENT:
181         if (!no_error && param!=1 && param!=2 && param!=4 && param!=8)
182            goto invalid_value_error;
183         ctx->Unpack.Alignment = param;
184         break;
185      case GL_UNPACK_COMPRESSED_BLOCK_WIDTH:
186         if (!no_error && !_mesa_is_desktop_gl(ctx))
187            goto invalid_enum_error;
188         if (!no_error && param<0)
189            goto invalid_value_error;
190         ctx->Unpack.CompressedBlockWidth = param;
191         break;
192      case GL_UNPACK_COMPRESSED_BLOCK_HEIGHT:
193         if (!no_error && !_mesa_is_desktop_gl(ctx))
194            goto invalid_enum_error;
195         if (!no_error && param<0)
196            goto invalid_value_error;
197         ctx->Unpack.CompressedBlockHeight = param;
198         break;
199      case GL_UNPACK_COMPRESSED_BLOCK_DEPTH:
200         if (!no_error && !_mesa_is_desktop_gl(ctx))
201            goto invalid_enum_error;
202         if (!no_error && param<0)
203            goto invalid_value_error;
204         ctx->Unpack.CompressedBlockDepth = param;
205         break;
206      case GL_UNPACK_COMPRESSED_BLOCK_SIZE:
207         if (!no_error && !_mesa_is_desktop_gl(ctx))
208            goto invalid_enum_error;
209         if (!no_error && param<0)
210            goto invalid_value_error;
211         ctx->Unpack.CompressedBlockSize = param;
212         break;
213      default:
214         if (!no_error)
215            goto invalid_enum_error;
216         else
217            unreachable("invalid pixel store enum");
218   }
219
220   return;
221
222invalid_enum_error:
223   _mesa_error(ctx, GL_INVALID_ENUM, "glPixelStore");
224   return;
225
226invalid_value_error:
227   _mesa_error(ctx, GL_INVALID_VALUE, "glPixelStore(param)");
228   return;
229}
230
231
232void GLAPIENTRY
233_mesa_PixelStorei(GLenum pname, GLint param)
234{
235   pixel_storei(pname, param, false);
236}
237
238
239void GLAPIENTRY
240_mesa_PixelStoref(GLenum pname, GLfloat param)
241{
242   _mesa_PixelStorei(pname, lroundf(param));
243}
244
245
246void GLAPIENTRY
247_mesa_PixelStorei_no_error(GLenum pname, GLint param)
248{
249   pixel_storei(pname, param, true);
250}
251
252
253void GLAPIENTRY
254_mesa_PixelStoref_no_error(GLenum pname, GLfloat param)
255{
256   _mesa_PixelStorei_no_error(pname, lroundf(param));
257}
258
259
260/**
261 * Initialize the context's pixel store state.
262 */
263void
264_mesa_init_pixelstore(struct gl_context *ctx)
265{
266   /* Pixel transfer */
267   ctx->Pack.Alignment = 4;
268   ctx->Pack.RowLength = 0;
269   ctx->Pack.ImageHeight = 0;
270   ctx->Pack.SkipPixels = 0;
271   ctx->Pack.SkipRows = 0;
272   ctx->Pack.SkipImages = 0;
273   ctx->Pack.SwapBytes = GL_FALSE;
274   ctx->Pack.LsbFirst = GL_FALSE;
275   ctx->Pack.Invert = GL_FALSE;
276   ctx->Pack.CompressedBlockWidth = 0;
277   ctx->Pack.CompressedBlockHeight = 0;
278   ctx->Pack.CompressedBlockDepth = 0;
279   ctx->Pack.CompressedBlockSize = 0;
280   _mesa_reference_buffer_object(ctx, &ctx->Pack.BufferObj, NULL);
281   ctx->Unpack.Alignment = 4;
282   ctx->Unpack.RowLength = 0;
283   ctx->Unpack.ImageHeight = 0;
284   ctx->Unpack.SkipPixels = 0;
285   ctx->Unpack.SkipRows = 0;
286   ctx->Unpack.SkipImages = 0;
287   ctx->Unpack.SwapBytes = GL_FALSE;
288   ctx->Unpack.LsbFirst = GL_FALSE;
289   ctx->Unpack.Invert = GL_FALSE;
290   ctx->Unpack.CompressedBlockWidth = 0;
291   ctx->Unpack.CompressedBlockHeight = 0;
292   ctx->Unpack.CompressedBlockDepth = 0;
293   ctx->Unpack.CompressedBlockSize = 0;
294   _mesa_reference_buffer_object(ctx, &ctx->Unpack.BufferObj, NULL);
295
296   /*
297    * _mesa_unpack_image() returns image data in this format.  When we
298    * execute image commands (glDrawPixels(), glTexImage(), etc) from
299    * within display lists we have to be sure to set the current
300    * unpacking parameters to these values!
301    */
302   ctx->DefaultPacking.Alignment = 1;
303   ctx->DefaultPacking.RowLength = 0;
304   ctx->DefaultPacking.SkipPixels = 0;
305   ctx->DefaultPacking.SkipRows = 0;
306   ctx->DefaultPacking.ImageHeight = 0;
307   ctx->DefaultPacking.SkipImages = 0;
308   ctx->DefaultPacking.SwapBytes = GL_FALSE;
309   ctx->DefaultPacking.LsbFirst = GL_FALSE;
310   ctx->DefaultPacking.Invert = GL_FALSE;
311   _mesa_reference_buffer_object(ctx, &ctx->DefaultPacking.BufferObj, NULL);
312}
313
314
315/**
316 * Check if the given compressed pixel storage parameters are legal.
317 * Record a GL error if illegal.
318 * \return  true if legal, false if illegal
319 */
320bool
321_mesa_compressed_pixel_storage_error_check(
322   struct gl_context *ctx,
323   GLint dimensions,
324   const struct gl_pixelstore_attrib *packing,
325   const char *caller)
326{
327   if (!_mesa_is_desktop_gl(ctx) || !packing->CompressedBlockSize)
328      return true;
329
330   if (packing->CompressedBlockWidth &&
331       packing->SkipPixels % packing->CompressedBlockWidth) {
332      _mesa_error(ctx, GL_INVALID_OPERATION,
333                  "%s(skip-pixels %% block-width)", caller);
334      return false;
335   }
336
337   if (dimensions > 1 &&
338       packing->CompressedBlockHeight &&
339       packing->SkipRows % packing->CompressedBlockHeight) {
340      _mesa_error(ctx, GL_INVALID_OPERATION,
341                  "%s(skip-rows %% block-height)", caller);
342      return false;
343   }
344
345   if (dimensions > 2 &&
346       packing->CompressedBlockDepth &&
347       packing->SkipImages % packing->CompressedBlockDepth) {
348      _mesa_error(ctx, GL_INVALID_OPERATION,
349                  "%s(skip-images %% block-depth)", caller);
350      return false;
351   }
352
353   return true;
354}
355