summaryrefslogtreecommitdiff
path: root/src/mesa/main/querymatrix.c
blob: 944ad435f7a6aabd4f27be4b590758ce5317824f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/**************************************************************************
 *
 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
 * All Rights Reserved.
 *
 **************************************************************************/


/**
 * Code to implement GL_OES_query_matrix.  See the spec at:
 * http://www.khronos.org/registry/gles/extensions/OES/OES_query_matrix.txt
 */


#include <stdlib.h>
#include <math.h>
#include "GLES/gl.h"
#include "GLES/glext.h"


/**
 * This is from the GL_OES_query_matrix extension specification:
 *
 *  GLbitfield glQueryMatrixxOES( GLfixed mantissa[16],
 *                                GLint   exponent[16] )
 *  mantissa[16] contains the contents of the current matrix in GLfixed
 *  format.  exponent[16] contains the unbiased exponents applied to the
 *  matrix components, so that the internal representation of component i
 *  is close to mantissa[i] * 2^exponent[i].  The function returns a status
 *  word which is zero if all the components are valid. If
 *  status & (1<<i) != 0, the component i is invalid (e.g., NaN, Inf).
 *  The implementations are not required to keep track of overflows.  In
 *  that case, the invalid bits are never set.
 */

#define INT_TO_FIXED(x) ((GLfixed) ((x) << 16))
#define FLOAT_TO_FIXED(x) ((GLfixed) ((x) * 65536.0))

#if defined(_MSC_VER)
/* Oddly, the fpclassify() function doesn't exist in such a form
 * on MSVC.  This is an implementation using slightly different
 * lower-level Windows functions.
 */
#include <float.h>

enum {FP_NAN, FP_INFINITE, FP_ZERO, FP_SUBNORMAL, FP_NORMAL}
fpclassify(double x)
{
    switch(_fpclass(x)) {
        case _FPCLASS_SNAN: /* signaling NaN */
        case _FPCLASS_QNAN: /* quiet NaN */
            return FP_NAN;
        case _FPCLASS_NINF: /* negative infinity */
        case _FPCLASS_PINF: /* positive infinity */
            return FP_INFINITE;
        case _FPCLASS_NN:   /* negative normal */
        case _FPCLASS_PN:   /* positive normal */
            return FP_NORMAL;
        case _FPCLASS_ND:   /* negative denormalized */
        case _FPCLASS_PD:   /* positive denormalized */
            return FP_SUBNORMAL;
        case _FPCLASS_NZ:   /* negative zero */
        case _FPCLASS_PZ:   /* positive zero */
            return FP_ZERO;
        default:
            /* Should never get here; but if we do, this will guarantee
             * that the pattern is not treated like a number.
             */
            return FP_NAN;
    }
}

#elif defined(__APPLE__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
     defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
     (defined(__sun) && defined(__C99FEATURES__)) || defined(__MINGW32__) || \
     (defined(__sun) && defined(__GNUC__))

/* fpclassify is available. */

#elif !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600

enum {FP_NAN, FP_INFINITE, FP_ZERO, FP_SUBNORMAL, FP_NORMAL}
fpclassify(double x)
{
   /* XXX do something better someday */
   return FP_NORMAL;
}

#endif

extern GLbitfield GL_APIENTRY _es_QueryMatrixxOES(GLfixed mantissa[16], GLint exponent[16]);

/* The Mesa functions we'll need */
extern void GL_APIENTRY _mesa_GetIntegerv(GLenum pname, GLint *params);
extern void GL_APIENTRY _mesa_GetFloatv(GLenum pname, GLfloat *params);

GLbitfield GL_APIENTRY _es_QueryMatrixxOES(GLfixed mantissa[16], GLint exponent[16])
{
    GLfloat matrix[16];
    GLint tmp;
    GLenum currentMode = GL_FALSE;
    GLenum desiredMatrix = GL_FALSE;
    /* The bitfield returns 1 for each component that is invalid (i.e.
     * NaN or Inf).  In case of error, everything is invalid.
     */
    GLbitfield rv;
    register unsigned int i;
    unsigned int bit;

    /* This data structure defines the mapping between the current matrix
     * mode and the desired matrix identifier.
     */
    static struct {
        GLenum currentMode;
        GLenum desiredMatrix;
    } modes[] = {
        {GL_MODELVIEW, GL_MODELVIEW_MATRIX},
        {GL_PROJECTION, GL_PROJECTION_MATRIX},
        {GL_TEXTURE, GL_TEXTURE_MATRIX},
    };

    /* Call Mesa to get the current matrix in floating-point form.  First,
     * we have to figure out what the current matrix mode is.
     */
    _mesa_GetIntegerv(GL_MATRIX_MODE, &tmp);
    currentMode = (GLenum) tmp;

    /* The mode is either GL_FALSE, if for some reason we failed to query
     * the mode, or a given mode from the above table.  Search for the
     * returned mode to get the desired matrix; if we don't find it,
     * we can return immediately, as _mesa_GetInteger() will have
     * logged the necessary error already.
     */
    for (i = 0; i < sizeof(modes)/sizeof(modes[0]); i++) {
        if (modes[i].currentMode == currentMode) {
            desiredMatrix = modes[i].desiredMatrix;
            break;
        }
    }
    if (desiredMatrix == GL_FALSE) {
        /* Early error means all values are invalid. */
        return 0xffff;
    }

    /* Now pull the matrix itself. */
    _mesa_GetFloatv(desiredMatrix, matrix);

    rv = 0;
    for (i = 0, bit = 1; i < 16; i++, bit<<=1) {
        float normalizedFraction;
        int exp;

        switch (fpclassify(matrix[i])) {
            /* A "subnormal" or denormalized number is too small to be
             * represented in normal format; but despite that it's a
             * valid floating point number.  FP_ZERO and FP_NORMAL
             * are both valid as well.  We should be fine treating
             * these three cases as legitimate floating-point numbers.
             */
            case FP_SUBNORMAL:
            case FP_NORMAL:
            case FP_ZERO:
                normalizedFraction = (GLfloat)frexp(matrix[i], &exp);
                mantissa[i] = FLOAT_TO_FIXED(normalizedFraction);
                exponent[i] = (GLint) exp;
                break;

            /* If the entry is not-a-number or an infinity, then the
             * matrix component is invalid.  The invalid flag for
             * the component is already set; might as well set the
             * other return values to known values.  We'll set
             * distinct values so that a savvy end user could determine
             * whether the matrix component was a NaN or an infinity,
             * but this is more useful for debugging than anything else
             * since the standard doesn't specify any such magic
             * values to return.
             */
            case FP_NAN:
                mantissa[i] = INT_TO_FIXED(0);
                exponent[i] = (GLint) 0;
                rv |= bit;
                break;

            case FP_INFINITE:
                /* Return +/- 1 based on whether it's a positive or
                 * negative infinity.
                 */
                if (matrix[i] > 0) {
                    mantissa[i] = INT_TO_FIXED(1);
                }
                else {
                    mantissa[i] = -INT_TO_FIXED(1);
                }
                exponent[i] = (GLint) 0;
                rv |= bit;
                break;

            /* We should never get here; but here's a catching case
             * in case fpclassify() is returnings something unexpected.
             */
            default:
                mantissa[i] = INT_TO_FIXED(2);
                exponent[i] = (GLint) 0;
                rv |= bit;
                break;
        }

    } /* for each component */

    /* All done */
    return rv;
}