+ ASSERT(tObj->WrapS == GL_CLAMP ||
+ tObj->WrapS == GL_CLAMP_TO_EDGE ||
+ tObj->WrapS == GL_CLAMP_TO_BORDER_ARB);
+ ASSERT(tObj->WrapT == GL_CLAMP ||
+ tObj->WrapT == GL_CLAMP_TO_EDGE ||
+ tObj->WrapT == GL_CLAMP_TO_BORDER_ARB);
+ ASSERT(img->Format != GL_COLOR_INDEX);
+
+ /* XXX move Wrap mode tests outside of loops for common cases */
+ for (i = 0; i < n; i++) {
+ GLint row, col;
+ /* NOTE: we DO NOT use [0, 1] texture coordinates! */
+ if (tObj->WrapS == GL_CLAMP) {
+ col = IFLOOR( CLAMP(texcoords[i][0], 0.0F, width) );
+ }
+ else if (tObj->WrapS == GL_CLAMP_TO_EDGE) {
+ col = IFLOOR( CLAMP(texcoords[i][0], 0.5F, width - 0.5F) );
+ }
+ else {
+ col = IFLOOR( CLAMP(texcoords[i][0], -0.5F, width + 0.5F) );
+ }
+ if (tObj->WrapT == GL_CLAMP) {
+ row = IFLOOR( CLAMP(texcoords[i][1], 0.0F, height) );
+ }
+ else if (tObj->WrapT == GL_CLAMP_TO_EDGE) {
+ row = IFLOOR( CLAMP(texcoords[i][1], 0.5F, height - 0.5F) );
+ }
+ else {
+ row = IFLOOR( CLAMP(texcoords[i][1], -0.5F, height + 0.5F) );
+ }
+
+ col = CLAMP(col, 0, width_minus_1);
+ row = CLAMP(row, 0, height_minus_1);
+
+ (*img->FetchTexel)(img, col, row, 0, (GLvoid *) rgba[i]);
+ }
+}
+
+
+static void
+sample_linear_rect(GLcontext *ctx, GLuint texUnit,
+ const struct gl_texture_object *tObj, GLuint n,
+ GLfloat texcoords[][4],
+ const GLfloat lambda[], GLchan rgba[][4])
+{
+ const struct gl_texture_image *img = tObj->Image[0];
+ const GLfloat width = (GLfloat) img->Width;
+ const GLfloat height = (GLfloat) img->Height;
+ const GLint width_minus_1 = img->Width - 1;
+ const GLint height_minus_1 = img->Height - 1;
+ GLuint i;
+
+ (void) texUnit;
+ (void) lambda;
+
+ ASSERT(tObj->WrapS == GL_CLAMP ||
+ tObj->WrapS == GL_CLAMP_TO_EDGE ||
+ tObj->WrapS == GL_CLAMP_TO_BORDER_ARB);
+ ASSERT(tObj->WrapT == GL_CLAMP ||
+ tObj->WrapT == GL_CLAMP_TO_EDGE ||
+ tObj->WrapT == GL_CLAMP_TO_BORDER_ARB);
+ ASSERT(img->Format != GL_COLOR_INDEX);
+
+ /* XXX lots of opportunity for optimization in this loop */
+ for (i = 0; i < n; i++) {
+ GLfloat frow, fcol;
+ GLint row0, col0, row1, col1;
+ GLchan t00[4], t01[4], t10[4], t11[4];
+ GLfloat a, b, w00, w01, w10, w11;
+
+ /* NOTE: we DO NOT use [0, 1] texture coordinates! */
+ if (tObj->WrapS == GL_CLAMP) {
+ fcol = CLAMP(texcoords[i][0], 0.0F, width);
+ }
+ else if (tObj->WrapS == GL_CLAMP_TO_EDGE) {
+ fcol = CLAMP(texcoords[i][0], 0.5F, width - 0.5F);
+ }
+ else {
+ fcol = CLAMP(texcoords[i][0], -0.5F, width + 0.5F);
+ }
+ if (tObj->WrapT == GL_CLAMP) {
+ frow = CLAMP(texcoords[i][1], 0.0F, height);
+ }
+ else if (tObj->WrapT == GL_CLAMP_TO_EDGE) {
+ frow = CLAMP(texcoords[i][1], 0.5F, height - 0.5F);
+ }
+ else {
+ frow = CLAMP(texcoords[i][1], -0.5F, height + 0.5F);
+ }
+
+ /* compute integer rows/columns */
+ col0 = IFLOOR(fcol);
+ col1 = col0 + 1;
+ col0 = CLAMP(col0, 0, width_minus_1);
+ col1 = CLAMP(col1, 0, width_minus_1);
+ row0 = IFLOOR(frow);
+ row1 = row0 + 1;
+ row0 = CLAMP(row0, 0, height_minus_1);
+ row1 = CLAMP(row1, 0, height_minus_1);
+
+ /* get four texel samples */
+ (*img->FetchTexel)(img, col0, row0, 0, (GLvoid *) t00);
+ (*img->FetchTexel)(img, col1, row0, 0, (GLvoid *) t10);
+ (*img->FetchTexel)(img, col0, row1, 0, (GLvoid *) t01);
+ (*img->FetchTexel)(img, col1, row1, 0, (GLvoid *) t11);
+
+ /* compute sample weights */
+ a = FRAC(fcol);
+ b = FRAC(frow);
+ w00 = (1.0F-a) * (1.0F-b);
+ w10 = a * (1.0F-b);
+ w01 = (1.0F-a) * b ;
+ w11 = a * b ;
+
+ /* compute weighted average of samples */
+ rgba[i][0] =
+ (GLchan) (w00 * t00[0] + w10 * t10[0] + w01 * t01[0] + w11 * t11[0]);
+ rgba[i][1] =
+ (GLchan) (w00 * t00[1] + w10 * t10[1] + w01 * t01[1] + w11 * t11[1]);
+ rgba[i][2] =
+ (GLchan) (w00 * t00[2] + w10 * t10[2] + w01 * t01[2] + w11 * t11[2]);
+ rgba[i][3] =
+ (GLchan) (w00 * t00[3] + w10 * t10[3] + w01 * t01[3] + w11 * t11[3]);
+ }
+}
+
+
+static void
+sample_lambda_rect( GLcontext *ctx, GLuint texUnit,
+ const struct gl_texture_object *tObj, GLuint n,
+ GLfloat texcoords[][4], const GLfloat lambda[],
+ GLchan rgba[][4])
+{
+ GLuint minStart, minEnd, magStart, magEnd;
+
+ /* We only need lambda to decide between minification and magnification.
+ * There is no mipmapping with rectangular textures.
+ */
+ compute_min_mag_ranges(SWRAST_CONTEXT(ctx)->_MinMagThresh[texUnit],
+ n, lambda, &minStart, &minEnd, &magStart, &magEnd);
+
+ if (minStart < minEnd) {
+ if (tObj->MinFilter == GL_NEAREST) {
+ sample_nearest_rect( ctx, texUnit, tObj, minEnd - minStart,
+ texcoords + minStart, NULL, rgba + minStart);
+ }
+ else {
+ sample_linear_rect( ctx, texUnit, tObj, minEnd - minStart,
+ texcoords + minStart, NULL, rgba + minStart);
+ }
+ }
+ if (magStart < magEnd) {
+ if (tObj->MagFilter == GL_NEAREST) {
+ sample_nearest_rect( ctx, texUnit, tObj, magEnd - magStart,
+ texcoords + magStart, NULL, rgba + magStart);
+ }
+ else {
+ sample_linear_rect( ctx, texUnit, tObj, magEnd - magStart,
+ texcoords + magStart, NULL, rgba + magStart);
+ }
+ }
+}
+
+
+
+/*
+ * Sample a shadow/depth texture.
+ */
+static void
+sample_depth_texture( GLcontext *ctx, GLuint unit,
+ const struct gl_texture_object *tObj, GLuint n,
+ GLfloat texcoords[][4], const GLfloat lambda[],
+ GLchan texel[][4] )
+{
+ const GLint baseLevel = tObj->BaseLevel;
+ const struct gl_texture_image *texImage = tObj->Image[baseLevel];
+ const GLuint width = texImage->Width;
+ const GLuint height = texImage->Height;
+ GLchan ambient;
+ GLenum function;
+ GLchan result;
+
+ (void) unit;
+
+ ASSERT(tObj->Image[tObj->BaseLevel]->Format == GL_DEPTH_COMPONENT);
+ ASSERT(tObj->Target == GL_TEXTURE_1D ||
+ tObj->Target == GL_TEXTURE_2D ||
+ tObj->Target == GL_TEXTURE_RECTANGLE_NV);
+
+ UNCLAMPED_FLOAT_TO_CHAN(ambient, tObj->ShadowAmbient);
+
+ /* XXXX if tObj->MinFilter != tObj->MagFilter, we're ignoring lambda */
+
+ /* XXX this could be precomputed and saved in the texture object */
+ if (tObj->CompareFlag) {
+ /* GL_SGIX_shadow */
+ if (tObj->CompareOperator == GL_TEXTURE_LEQUAL_R_SGIX) {
+ function = GL_LEQUAL;
+ }
+ else {
+ ASSERT(tObj->CompareOperator == GL_TEXTURE_GEQUAL_R_SGIX);
+ function = GL_GEQUAL;
+ }
+ }
+ else if (tObj->CompareMode == GL_COMPARE_R_TO_TEXTURE_ARB) {
+ /* GL_ARB_shadow */
+ function = tObj->CompareFunc;
+ }
+ else {
+ function = GL_NONE; /* pass depth through as grayscale */
+ }
+
+ if (tObj->MagFilter == GL_NEAREST) {
+ GLuint i;
+ for (i = 0; i < n; i++) {
+ GLfloat depthSample;
+ GLint col, row;
+ /* XXX fix for texture rectangle! */
+ COMPUTE_NEAREST_TEXEL_LOCATION(tObj->WrapS, texcoords[i][0], width, col);
+ COMPUTE_NEAREST_TEXEL_LOCATION(tObj->WrapT, texcoords[i][1], height, row);
+ depthSample = *((const GLfloat *) texImage->Data + row * width + col);
+
+ switch (function) {
+ case GL_LEQUAL:
+ result = (texcoords[i][2] <= depthSample) ? CHAN_MAX : ambient;
+ break;
+ case GL_GEQUAL:
+ result = (texcoords[i][2] >= depthSample) ? CHAN_MAX : ambient;
+ break;
+ case GL_LESS:
+ result = (texcoords[i][2] < depthSample) ? CHAN_MAX : ambient;
+ break;
+ case GL_GREATER:
+ result = (texcoords[i][2] > depthSample) ? CHAN_MAX : ambient;
+ break;
+ case GL_EQUAL:
+ result = (texcoords[i][2] == depthSample) ? CHAN_MAX : ambient;
+ break;
+ case GL_NOTEQUAL:
+ result = (texcoords[i][2] != depthSample) ? CHAN_MAX : ambient;
+ break;
+ case GL_ALWAYS:
+ result = CHAN_MAX;
+ break;
+ case GL_NEVER:
+ result = ambient;
+ break;
+ case GL_NONE:
+ CLAMPED_FLOAT_TO_CHAN(result, depthSample);
+ break;
+ default:
+ _mesa_problem(ctx, "Bad compare func in sample_depth_texture");
+ return;
+ }
+
+ switch (tObj->DepthMode) {
+ case GL_LUMINANCE:
+ texel[i][RCOMP] = result;
+ texel[i][GCOMP] = result;
+ texel[i][BCOMP] = result;
+ texel[i][ACOMP] = CHAN_MAX;
+ break;
+ case GL_INTENSITY:
+ texel[i][RCOMP] = result;
+ texel[i][GCOMP] = result;
+ texel[i][BCOMP] = result;
+ texel[i][ACOMP] = result;
+ break;
+ case GL_ALPHA:
+ texel[i][RCOMP] = 0;
+ texel[i][GCOMP] = 0;
+ texel[i][BCOMP] = 0;
+ texel[i][ACOMP] = result;
+ break;
+ default:
+ _mesa_problem(ctx, "Bad depth texture mode");
+ }
+ }
+ }
+ else {
+ GLuint i;
+ ASSERT(tObj->MagFilter == GL_LINEAR);
+ for (i = 0; i < n; i++) {
+ GLfloat depth00, depth01, depth10, depth11;
+ GLint i0, i1, j0, j1;
+ GLfloat u, v;
+ GLuint useBorderTexel;
+
+ /* XXX fix for texture rectangle! */
+ COMPUTE_LINEAR_TEXEL_LOCATIONS(tObj->WrapS, texcoords[i][0], u, width, i0, i1);
+ COMPUTE_LINEAR_TEXEL_LOCATIONS(tObj->WrapT, texcoords[i][1], v, height,j0, j1);
+
+ useBorderTexel = 0;
+ if (texImage->Border) {
+ i0 += texImage->Border;
+ i1 += texImage->Border;
+ j0 += texImage->Border;
+ j1 += texImage->Border;
+ }
+ else {
+ if (i0 < 0 || i0 >= (GLint) width) useBorderTexel |= I0BIT;
+ if (i1 < 0 || i1 >= (GLint) width) useBorderTexel |= I1BIT;
+ if (j0 < 0 || j0 >= (GLint) height) useBorderTexel |= J0BIT;
+ if (j1 < 0 || j1 >= (GLint) height) useBorderTexel |= J1BIT;
+ }
+
+ /* get four depth samples from the texture */
+ if (useBorderTexel & (I0BIT | J0BIT)) {
+ depth00 = 1.0;
+ }
+ else {
+ depth00 = *((const GLfloat *) texImage->Data + j0 * width + i0);
+ }
+ if (useBorderTexel & (I1BIT | J0BIT)) {
+ depth10 = 1.0;
+ }
+ else {
+ depth10 = *((const GLfloat *) texImage->Data + j0 * width + i1);
+ }
+ if (useBorderTexel & (I0BIT | J1BIT)) {
+ depth01 = 1.0;
+ }
+ else {
+ depth01 = *((const GLfloat *) texImage->Data + j1 * width + i0);
+ }
+ if (useBorderTexel & (I1BIT | J1BIT)) {
+ depth11 = 1.0;
+ }
+ else {
+ depth11 = *((const GLfloat *) texImage->Data + j1 * width + i1);
+ }
+
+ if (0) {
+ /* compute a single weighted depth sample and do one comparison */
+ const GLfloat a = FRAC(u + 1.0F);
+ const GLfloat b = FRAC(v + 1.0F);
+ const GLfloat w00 = (1.0F - a) * (1.0F - b);
+ const GLfloat w10 = ( a) * (1.0F - b);
+ const GLfloat w01 = (1.0F - a) * ( b);
+ const GLfloat w11 = ( a) * ( b);
+ const GLfloat depthSample = w00 * depth00 + w10 * depth10
+ + w01 * depth01 + w11 * depth11;
+ if ((depthSample <= texcoords[i][2] && function == GL_LEQUAL) ||
+ (depthSample >= texcoords[i][2] && function == GL_GEQUAL)) {
+ result = ambient;
+ }
+ else {
+ result = CHAN_MAX;
+ }
+ }
+ else {
+ /* Do four depth/R comparisons and compute a weighted result.
+ * If this touches on somebody's I.P., I'll remove this code
+ * upon request.
+ */
+ const GLfloat d = (CHAN_MAXF - (GLfloat) ambient) * 0.25F;
+ GLfloat luminance = CHAN_MAXF;
+
+ switch (function) {
+ case GL_LEQUAL:
+ if (depth00 <= texcoords[i][2]) luminance -= d;
+ if (depth01 <= texcoords[i][2]) luminance -= d;
+ if (depth10 <= texcoords[i][2]) luminance -= d;
+ if (depth11 <= texcoords[i][2]) luminance -= d;
+ result = (GLchan) luminance;
+ break;
+ case GL_GEQUAL:
+ if (depth00 >= texcoords[i][2]) luminance -= d;
+ if (depth01 >= texcoords[i][2]) luminance -= d;
+ if (depth10 >= texcoords[i][2]) luminance -= d;
+ if (depth11 >= texcoords[i][2]) luminance -= d;
+ result = (GLchan) luminance;
+ break;
+ case GL_LESS:
+ if (depth00 < texcoords[i][2]) luminance -= d;
+ if (depth01 < texcoords[i][2]) luminance -= d;
+ if (depth10 < texcoords[i][2]) luminance -= d;
+ if (depth11 < texcoords[i][2]) luminance -= d;
+ result = (GLchan) luminance;
+ break;
+ case GL_GREATER:
+ if (depth00 > texcoords[i][2]) luminance -= d;
+ if (depth01 > texcoords[i][2]) luminance -= d;
+ if (depth10 > texcoords[i][2]) luminance -= d;
+ if (depth11 > texcoords[i][2]) luminance -= d;
+ result = (GLchan) luminance;
+ break;
+ case GL_EQUAL:
+ if (depth00 == texcoords[i][2]) luminance -= d;
+ if (depth01 == texcoords[i][2]) luminance -= d;
+ if (depth10 == texcoords[i][2]) luminance -= d;
+ if (depth11 == texcoords[i][2]) luminance -= d;
+ result = (GLchan) luminance;
+ break;
+ case GL_NOTEQUAL:
+ if (depth00 != texcoords[i][2]) luminance -= d;
+ if (depth01 != texcoords[i][2]) luminance -= d;
+ if (depth10 != texcoords[i][2]) luminance -= d;
+ if (depth11 != texcoords[i][2]) luminance -= d;
+ result = (GLchan) luminance;
+ break;
+ case GL_ALWAYS:
+ result = 0;
+ break;
+ case GL_NEVER:
+ result = CHAN_MAX;
+ break;
+ case GL_NONE:
+ /* ordinary bilinear filtering */
+ {
+ const GLfloat a = FRAC(u + 1.0F);
+ const GLfloat b = FRAC(v + 1.0F);
+ const GLfloat w00 = (1.0F - a) * (1.0F - b);
+ const GLfloat w10 = ( a) * (1.0F - b);
+ const GLfloat w01 = (1.0F - a) * ( b);
+ const GLfloat w11 = ( a) * ( b);
+ const GLfloat depthSample = w00 * depth00 + w10 * depth10
+ + w01 * depth01 + w11 * depth11;
+ CLAMPED_FLOAT_TO_CHAN(result, depthSample);
+ }
+ break;
+ default:
+ _mesa_problem(ctx, "Bad compare func in sample_depth_texture");
+ return;
+ }
+ }
+
+ switch (tObj->DepthMode) {
+ case GL_LUMINANCE:
+ texel[i][RCOMP] = result;
+ texel[i][GCOMP] = result;
+ texel[i][BCOMP] = result;
+ texel[i][ACOMP] = CHAN_MAX;
+ break;
+ case GL_INTENSITY:
+ texel[i][RCOMP] = result;
+ texel[i][GCOMP] = result;
+ texel[i][BCOMP] = result;
+ texel[i][ACOMP] = result;
+ break;
+ case GL_ALPHA:
+ texel[i][RCOMP] = 0;
+ texel[i][GCOMP] = 0;
+ texel[i][BCOMP] = 0;
+ texel[i][ACOMP] = result;
+ break;
+ default:
+ _mesa_problem(ctx, "Bad depth texture mode");
+ }
+ } /* for */
+ } /* if filter */
+}
+
+
+#if 0
+/*
+ * Experimental depth texture sampling function.
+ */
+static void
+sample_depth_texture2(const GLcontext *ctx,
+ const struct gl_texture_unit *texUnit,
+ GLuint n, GLfloat texcoords[][4],
+ GLchan texel[][4])
+{
+ const struct gl_texture_object *texObj = texUnit->_Current;
+ const GLint baseLevel = texObj->BaseLevel;
+ const struct gl_texture_image *texImage = texObj->Image[baseLevel];
+ const GLuint width = texImage->Width;
+ const GLuint height = texImage->Height;
+ GLchan ambient;
+ GLboolean lequal, gequal;
+
+ if (texObj->Target != GL_TEXTURE_2D) {
+ _mesa_problem(ctx, "only 2-D depth textures supported at this time");
+ return;
+ }
+
+ if (texObj->MinFilter != texObj->MagFilter) {
+ _mesa_problem(ctx, "mipmapped depth textures not supported at this time");
+ return;
+ }
+
+ /* XXX the GL_SGIX_shadow extension spec doesn't say what to do if
+ * GL_TEXTURE_COMPARE_SGIX == GL_TRUE but the current texture object
+ * isn't a depth texture.
+ */
+ if (texImage->Format != GL_DEPTH_COMPONENT) {
+ _mesa_problem(ctx,"GL_TEXTURE_COMPARE_SGIX enabled with non-depth texture");
+ return;
+ }
+
+ UNCLAMPED_FLOAT_TO_CHAN(ambient, tObj->ShadowAmbient);
+
+ if (texObj->CompareOperator == GL_TEXTURE_LEQUAL_R_SGIX) {
+ lequal = GL_TRUE;
+ gequal = GL_FALSE;
+ }
+ else {
+ lequal = GL_FALSE;
+ gequal = GL_TRUE;
+ }
+
+ {
+ GLuint i;
+ for (i = 0; i < n; i++) {
+ const GLint K = 3;
+ GLint col, row, ii, jj, imin, imax, jmin, jmax, samples, count;
+ GLfloat w;
+ GLchan lum;
+ COMPUTE_NEAREST_TEXEL_LOCATION(texObj->WrapS, texcoords[i][0],
+ width, col);
+ COMPUTE_NEAREST_TEXEL_LOCATION(texObj->WrapT, texcoords[i][1],
+ height, row);
+
+ imin = col - K;
+ imax = col + K;
+ jmin = row - K;
+ jmax = row + K;
+
+ if (imin < 0) imin = 0;
+ if (imax >= width) imax = width - 1;
+ if (jmin < 0) jmin = 0;
+ if (jmax >= height) jmax = height - 1;
+
+ samples = (imax - imin + 1) * (jmax - jmin + 1);
+ count = 0;
+ for (jj = jmin; jj <= jmax; jj++) {
+ for (ii = imin; ii <= imax; ii++) {
+ GLfloat depthSample = *((const GLfloat *) texImage->Data
+ + jj * width + ii);
+ if ((depthSample <= r[i] && lequal) ||
+ (depthSample >= r[i] && gequal)) {
+ count++;
+ }
+ }
+ }
+
+ w = (GLfloat) count / (GLfloat) samples;
+ w = CHAN_MAXF - w * (CHAN_MAXF - (GLfloat) ambient);
+ lum = (GLint) w;
+
+ texel[i][RCOMP] = lum;
+ texel[i][GCOMP] = lum;
+ texel[i][BCOMP] = lum;
+ texel[i][ACOMP] = CHAN_MAX;
+ }
+ }
+}
+#endif
+
+
+/**
+ * We use this function when a texture object is in an "incomplete" state.
+ */
+static void
+null_sample_func( GLcontext *ctx, GLuint texUnit,
+ const struct gl_texture_object *tObj, GLuint n,
+ GLfloat texcoords[][4], const GLfloat lambda[],
+ GLchan rgba[][4])
+{
+}
+
+
+
+/**
+ * Setup the texture sampling function for this texture object.
+ */
+void
+_swrast_choose_texture_sample_func( GLcontext *ctx, GLuint texUnit,
+ const struct gl_texture_object *t )
+{
+ SWcontext *swrast = SWRAST_CONTEXT(ctx);
+
+ if (!t->Complete) {
+ swrast->TextureSample[texUnit] = null_sample_func;
+ }
+ else {
+ const GLboolean needLambda = (GLboolean) (t->MinFilter != t->MagFilter);
+ const GLenum format = t->Image[t->BaseLevel]->Format;
+
+ if (needLambda) {
+ /* Compute min/mag filter threshold */