mesa: fpclassify is available on OpenSolaris.
[mesa.git] / src / mesa / main / querymatrix.c
1 /**************************************************************************
2 *
3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 **************************************************************************/
7
8
9 /**
10 * Code to implement GL_OES_query_matrix. See the spec at:
11 * http://www.khronos.org/registry/gles/extensions/OES/OES_query_matrix.txt
12 */
13
14
15 #include <stdlib.h>
16 #include <math.h>
17 #include "GLES/gl.h"
18 #include "GLES/glext.h"
19
20
21 /**
22 * This is from the GL_OES_query_matrix extension specification:
23 *
24 * GLbitfield glQueryMatrixxOES( GLfixed mantissa[16],
25 * GLint exponent[16] )
26 * mantissa[16] contains the contents of the current matrix in GLfixed
27 * format. exponent[16] contains the unbiased exponents applied to the
28 * matrix components, so that the internal representation of component i
29 * is close to mantissa[i] * 2^exponent[i]. The function returns a status
30 * word which is zero if all the components are valid. If
31 * status & (1<<i) != 0, the component i is invalid (e.g., NaN, Inf).
32 * The implementations are not required to keep track of overflows. In
33 * that case, the invalid bits are never set.
34 */
35
36 #define INT_TO_FIXED(x) ((GLfixed) ((x) << 16))
37 #define FLOAT_TO_FIXED(x) ((GLfixed) ((x) * 65536.0))
38
39 #if defined(_MSC_VER)
40 /* Oddly, the fpclassify() function doesn't exist in such a form
41 * on MSVC. This is an implementation using slightly different
42 * lower-level Windows functions.
43 */
44 #include <float.h>
45
46 enum {FP_NAN, FP_INFINITE, FP_ZERO, FP_SUBNORMAL, FP_NORMAL}
47 fpclassify(double x)
48 {
49 switch(_fpclass(x)) {
50 case _FPCLASS_SNAN: /* signaling NaN */
51 case _FPCLASS_QNAN: /* quiet NaN */
52 return FP_NAN;
53 case _FPCLASS_NINF: /* negative infinity */
54 case _FPCLASS_PINF: /* positive infinity */
55 return FP_INFINITE;
56 case _FPCLASS_NN: /* negative normal */
57 case _FPCLASS_PN: /* positive normal */
58 return FP_NORMAL;
59 case _FPCLASS_ND: /* negative denormalized */
60 case _FPCLASS_PD: /* positive denormalized */
61 return FP_SUBNORMAL;
62 case _FPCLASS_NZ: /* negative zero */
63 case _FPCLASS_PZ: /* positive zero */
64 return FP_ZERO;
65 default:
66 /* Should never get here; but if we do, this will guarantee
67 * that the pattern is not treated like a number.
68 */
69 return FP_NAN;
70 }
71 }
72
73 #elif defined(__APPLE__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
74 defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
75 (defined(__sun) && defined(__C99FEATURES__)) || defined(__MINGW32__) || \
76 (defined(__sun) && defined(__GNUC__))
77
78 /* fpclassify is available. */
79
80 #elif !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600
81
82 enum {FP_NAN, FP_INFINITE, FP_ZERO, FP_SUBNORMAL, FP_NORMAL}
83 fpclassify(double x)
84 {
85 /* XXX do something better someday */
86 return FP_NORMAL;
87 }
88
89 #endif
90
91 extern GLbitfield GL_APIENTRY _es_QueryMatrixxOES(GLfixed mantissa[16], GLint exponent[16]);
92
93 /* The Mesa functions we'll need */
94 extern void GL_APIENTRY _mesa_GetIntegerv(GLenum pname, GLint *params);
95 extern void GL_APIENTRY _mesa_GetFloatv(GLenum pname, GLfloat *params);
96
97 GLbitfield GL_APIENTRY _es_QueryMatrixxOES(GLfixed mantissa[16], GLint exponent[16])
98 {
99 GLfloat matrix[16];
100 GLint tmp;
101 GLenum currentMode = GL_FALSE;
102 GLenum desiredMatrix = GL_FALSE;
103 /* The bitfield returns 1 for each component that is invalid (i.e.
104 * NaN or Inf). In case of error, everything is invalid.
105 */
106 GLbitfield rv;
107 register unsigned int i;
108 unsigned int bit;
109
110 /* This data structure defines the mapping between the current matrix
111 * mode and the desired matrix identifier.
112 */
113 static struct {
114 GLenum currentMode;
115 GLenum desiredMatrix;
116 } modes[] = {
117 {GL_MODELVIEW, GL_MODELVIEW_MATRIX},
118 {GL_PROJECTION, GL_PROJECTION_MATRIX},
119 {GL_TEXTURE, GL_TEXTURE_MATRIX},
120 #if 0
121 /* this doesn't exist in GLES */
122 {GL_COLOR, GL_COLOR_MATRIX},
123 #endif
124 };
125
126 /* Call Mesa to get the current matrix in floating-point form. First,
127 * we have to figure out what the current matrix mode is.
128 */
129 _mesa_GetIntegerv(GL_MATRIX_MODE, &tmp);
130 currentMode = (GLenum) tmp;
131
132 /* The mode is either GL_FALSE, if for some reason we failed to query
133 * the mode, or a given mode from the above table. Search for the
134 * returned mode to get the desired matrix; if we don't find it,
135 * we can return immediately, as _mesa_GetInteger() will have
136 * logged the necessary error already.
137 */
138 for (i = 0; i < sizeof(modes)/sizeof(modes[0]); i++) {
139 if (modes[i].currentMode == currentMode) {
140 desiredMatrix = modes[i].desiredMatrix;
141 break;
142 }
143 }
144 if (desiredMatrix == GL_FALSE) {
145 /* Early error means all values are invalid. */
146 return 0xffff;
147 }
148
149 /* Now pull the matrix itself. */
150 _mesa_GetFloatv(desiredMatrix, matrix);
151
152 rv = 0;
153 for (i = 0, bit = 1; i < 16; i++, bit<<=1) {
154 float normalizedFraction;
155 int exp;
156
157 switch (fpclassify(matrix[i])) {
158 /* A "subnormal" or denormalized number is too small to be
159 * represented in normal format; but despite that it's a
160 * valid floating point number. FP_ZERO and FP_NORMAL
161 * are both valid as well. We should be fine treating
162 * these three cases as legitimate floating-point numbers.
163 */
164 case FP_SUBNORMAL:
165 case FP_NORMAL:
166 case FP_ZERO:
167 normalizedFraction = (GLfloat)frexp(matrix[i], &exp);
168 mantissa[i] = FLOAT_TO_FIXED(normalizedFraction);
169 exponent[i] = (GLint) exp;
170 break;
171
172 /* If the entry is not-a-number or an infinity, then the
173 * matrix component is invalid. The invalid flag for
174 * the component is already set; might as well set the
175 * other return values to known values. We'll set
176 * distinct values so that a savvy end user could determine
177 * whether the matrix component was a NaN or an infinity,
178 * but this is more useful for debugging than anything else
179 * since the standard doesn't specify any such magic
180 * values to return.
181 */
182 case FP_NAN:
183 mantissa[i] = INT_TO_FIXED(0);
184 exponent[i] = (GLint) 0;
185 rv |= bit;
186 break;
187
188 case FP_INFINITE:
189 /* Return +/- 1 based on whether it's a positive or
190 * negative infinity.
191 */
192 if (matrix[i] > 0) {
193 mantissa[i] = INT_TO_FIXED(1);
194 }
195 else {
196 mantissa[i] = -INT_TO_FIXED(1);
197 }
198 exponent[i] = (GLint) 0;
199 rv |= bit;
200 break;
201
202 /* We should never get here; but here's a catching case
203 * in case fpclassify() is returnings something unexpected.
204 */
205 default:
206 mantissa[i] = INT_TO_FIXED(2);
207 exponent[i] = (GLint) 0;
208 rv |= bit;
209 break;
210 }
211
212 } /* for each component */
213
214 /* All done */
215 return rv;
216 }