diff options
Diffstat (limited to 'mesalib/src/mesa/swrast')
-rw-r--r-- | mesalib/src/mesa/swrast/s_drawpix.c | 2 | ||||
-rw-r--r-- | mesalib/src/mesa/swrast/s_texfilter.c | 7416 |
2 files changed, 3707 insertions, 3711 deletions
diff --git a/mesalib/src/mesa/swrast/s_drawpix.c b/mesalib/src/mesa/swrast/s_drawpix.c index 11c63457f..63bfa79b5 100644 --- a/mesalib/src/mesa/swrast/s_drawpix.c +++ b/mesalib/src/mesa/swrast/s_drawpix.c @@ -335,7 +335,7 @@ draw_stencil_pixels( struct gl_context *ctx, GLint x, GLint y, ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT; const GLvoid *source = _mesa_image_address2d(unpack, pixels, width, height, - GL_COLOR_INDEX, type, + GL_STENCIL_INDEX, type, row, skipPixels); _mesa_unpack_stencil_span(ctx, spanWidth, destType, values, type, source, unpack, diff --git a/mesalib/src/mesa/swrast/s_texfilter.c b/mesalib/src/mesa/swrast/s_texfilter.c index 237e5d28a..ad31e3778 100644 --- a/mesalib/src/mesa/swrast/s_texfilter.c +++ b/mesalib/src/mesa/swrast/s_texfilter.c @@ -1,3710 +1,3706 @@ -/*
- * Mesa 3-D graphics library
- * Version: 7.3
- *
- * Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-#include "main/glheader.h"
-#include "main/context.h"
-#include "main/colormac.h"
-#include "main/imports.h"
-
-#include "s_context.h"
-#include "s_texfilter.h"
-
-
-/*
- * Note, the FRAC macro has to work perfectly. Otherwise you'll sometimes
- * see 1-pixel bands of improperly weighted linear-filtered textures.
- * The tests/texwrap.c demo is a good test.
- * Also note, FRAC(x) doesn't truly return the fractional part of x for x < 0.
- * Instead, if x < 0 then FRAC(x) = 1 - true_frac(x).
- */
-#define FRAC(f) ((f) - IFLOOR(f))
-
-
-
-/**
- * Linear interpolation macro
- */
-#define LERP(T, A, B) ( (A) + (T) * ((B) - (A)) )
-
-
-/**
- * Do 2D/biliner interpolation of float values.
- * v00, v10, v01 and v11 are typically four texture samples in a square/box.
- * a and b are the horizontal and vertical interpolants.
- * It's important that this function is inlined when compiled with
- * optimization! If we find that's not true on some systems, convert
- * to a macro.
- */
-static INLINE GLfloat
-lerp_2d(GLfloat a, GLfloat b,
- GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11)
-{
- const GLfloat temp0 = LERP(a, v00, v10);
- const GLfloat temp1 = LERP(a, v01, v11);
- return LERP(b, temp0, temp1);
-}
-
-
-/**
- * Do 3D/trilinear interpolation of float values.
- * \sa lerp_2d
- */
-static INLINE GLfloat
-lerp_3d(GLfloat a, GLfloat b, GLfloat c,
- GLfloat v000, GLfloat v100, GLfloat v010, GLfloat v110,
- GLfloat v001, GLfloat v101, GLfloat v011, GLfloat v111)
-{
- const GLfloat temp00 = LERP(a, v000, v100);
- const GLfloat temp10 = LERP(a, v010, v110);
- const GLfloat temp01 = LERP(a, v001, v101);
- const GLfloat temp11 = LERP(a, v011, v111);
- const GLfloat temp0 = LERP(b, temp00, temp10);
- const GLfloat temp1 = LERP(b, temp01, temp11);
- return LERP(c, temp0, temp1);
-}
-
-
-/**
- * Do linear interpolation of colors.
- */
-static INLINE void
-lerp_rgba(GLfloat result[4], GLfloat t, const GLfloat a[4], const GLfloat b[4])
-{
- result[0] = LERP(t, a[0], b[0]);
- result[1] = LERP(t, a[1], b[1]);
- result[2] = LERP(t, a[2], b[2]);
- result[3] = LERP(t, a[3], b[3]);
-}
-
-
-/**
- * Do bilinear interpolation of colors.
- */
-static INLINE void
-lerp_rgba_2d(GLfloat result[4], GLfloat a, GLfloat b,
- const GLfloat t00[4], const GLfloat t10[4],
- const GLfloat t01[4], const GLfloat t11[4])
-{
- result[0] = lerp_2d(a, b, t00[0], t10[0], t01[0], t11[0]);
- result[1] = lerp_2d(a, b, t00[1], t10[1], t01[1], t11[1]);
- result[2] = lerp_2d(a, b, t00[2], t10[2], t01[2], t11[2]);
- result[3] = lerp_2d(a, b, t00[3], t10[3], t01[3], t11[3]);
-}
-
-
-/**
- * Do trilinear interpolation of colors.
- */
-static INLINE void
-lerp_rgba_3d(GLfloat result[4], GLfloat a, GLfloat b, GLfloat c,
- const GLfloat t000[4], const GLfloat t100[4],
- const GLfloat t010[4], const GLfloat t110[4],
- const GLfloat t001[4], const GLfloat t101[4],
- const GLfloat t011[4], const GLfloat t111[4])
-{
- GLuint k;
- /* compiler should unroll these short loops */
- for (k = 0; k < 4; k++) {
- result[k] = lerp_3d(a, b, c, t000[k], t100[k], t010[k], t110[k],
- t001[k], t101[k], t011[k], t111[k]);
- }
-}
-
-
-/**
- * Used for GL_REPEAT wrap mode. Using A % B doesn't produce the
- * right results for A<0. Casting to A to be unsigned only works if B
- * is a power of two. Adding a bias to A (which is a multiple of B)
- * avoids the problems with A < 0 (for reasonable A) without using a
- * conditional.
- */
-#define REMAINDER(A, B) (((A) + (B) * 1024) % (B))
-
-
-/**
- * Used to compute texel locations for linear sampling.
- * Input:
- * wrapMode = GL_REPEAT, GL_CLAMP, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER
- * s = texcoord in [0,1]
- * size = width (or height or depth) of texture
- * Output:
- * i0, i1 = returns two nearest texel indexes
- * weight = returns blend factor between texels
- */
-static INLINE void
-linear_texel_locations(GLenum wrapMode,
- const struct gl_texture_image *img,
- GLint size, GLfloat s,
- GLint *i0, GLint *i1, GLfloat *weight)
-{
- GLfloat u;
- switch (wrapMode) {
- case GL_REPEAT:
- u = s * size - 0.5F;
- if (img->_IsPowerOfTwo) {
- *i0 = IFLOOR(u) & (size - 1);
- *i1 = (*i0 + 1) & (size - 1);
- }
- else {
- *i0 = REMAINDER(IFLOOR(u), size);
- *i1 = REMAINDER(*i0 + 1, size);
- }
- break;
- case GL_CLAMP_TO_EDGE:
- if (s <= 0.0F)
- u = 0.0F;
- else if (s >= 1.0F)
- u = (GLfloat) size;
- else
- u = s * size;
- u -= 0.5F;
- *i0 = IFLOOR(u);
- *i1 = *i0 + 1;
- if (*i0 < 0)
- *i0 = 0;
- if (*i1 >= (GLint) size)
- *i1 = size - 1;
- break;
- case GL_CLAMP_TO_BORDER:
- {
- const GLfloat min = -1.0F / (2.0F * size);
- const GLfloat max = 1.0F - min;
- if (s <= min)
- u = min * size;
- else if (s >= max)
- u = max * size;
- else
- u = s * size;
- u -= 0.5F;
- *i0 = IFLOOR(u);
- *i1 = *i0 + 1;
- }
- break;
- case GL_MIRRORED_REPEAT:
- {
- const GLint flr = IFLOOR(s);
- if (flr & 1)
- u = 1.0F - (s - (GLfloat) flr);
- else
- u = s - (GLfloat) flr;
- u = (u * size) - 0.5F;
- *i0 = IFLOOR(u);
- *i1 = *i0 + 1;
- if (*i0 < 0)
- *i0 = 0;
- if (*i1 >= (GLint) size)
- *i1 = size - 1;
- }
- break;
- case GL_MIRROR_CLAMP_EXT:
- u = FABSF(s);
- if (u >= 1.0F)
- u = (GLfloat) size;
- else
- u *= size;
- u -= 0.5F;
- *i0 = IFLOOR(u);
- *i1 = *i0 + 1;
- break;
- case GL_MIRROR_CLAMP_TO_EDGE_EXT:
- u = FABSF(s);
- if (u >= 1.0F)
- u = (GLfloat) size;
- else
- u *= size;
- u -= 0.5F;
- *i0 = IFLOOR(u);
- *i1 = *i0 + 1;
- if (*i0 < 0)
- *i0 = 0;
- if (*i1 >= (GLint) size)
- *i1 = size - 1;
- break;
- case GL_MIRROR_CLAMP_TO_BORDER_EXT:
- {
- const GLfloat min = -1.0F / (2.0F * size);
- const GLfloat max = 1.0F - min;
- u = FABSF(s);
- if (u <= min)
- u = min * size;
- else if (u >= max)
- u = max * size;
- else
- u *= size;
- u -= 0.5F;
- *i0 = IFLOOR(u);
- *i1 = *i0 + 1;
- }
- break;
- case GL_CLAMP:
- if (s <= 0.0F)
- u = 0.0F;
- else if (s >= 1.0F)
- u = (GLfloat) size;
- else
- u = s * size;
- u -= 0.5F;
- *i0 = IFLOOR(u);
- *i1 = *i0 + 1;
- break;
- default:
- _mesa_problem(NULL, "Bad wrap mode");
- u = 0.0F;
- }
- *weight = FRAC(u);
-}
-
-
-/**
- * Used to compute texel location for nearest sampling.
- */
-static INLINE GLint
-nearest_texel_location(GLenum wrapMode,
- const struct gl_texture_image *img,
- GLint size, GLfloat s)
-{
- GLint i;
-
- switch (wrapMode) {
- case GL_REPEAT:
- /* s limited to [0,1) */
- /* i limited to [0,size-1] */
- i = IFLOOR(s * size);
- if (img->_IsPowerOfTwo)
- i &= (size - 1);
- else
- i = REMAINDER(i, size);
- return i;
- case GL_CLAMP_TO_EDGE:
- {
- /* s limited to [min,max] */
- /* i limited to [0, size-1] */
- const GLfloat min = 1.0F / (2.0F * size);
- const GLfloat max = 1.0F - min;
- if (s < min)
- i = 0;
- else if (s > max)
- i = size - 1;
- else
- i = IFLOOR(s * size);
- }
- return i;
- case GL_CLAMP_TO_BORDER:
- {
- /* s limited to [min,max] */
- /* i limited to [-1, size] */
- const GLfloat min = -1.0F / (2.0F * size);
- const GLfloat max = 1.0F - min;
- if (s <= min)
- i = -1;
- else if (s >= max)
- i = size;
- else
- i = IFLOOR(s * size);
- }
- return i;
- case GL_MIRRORED_REPEAT:
- {
- const GLfloat min = 1.0F / (2.0F * size);
- const GLfloat max = 1.0F - min;
- const GLint flr = IFLOOR(s);
- GLfloat u;
- if (flr & 1)
- u = 1.0F - (s - (GLfloat) flr);
- else
- u = s - (GLfloat) flr;
- if (u < min)
- i = 0;
- else if (u > max)
- i = size - 1;
- else
- i = IFLOOR(u * size);
- }
- return i;
- case GL_MIRROR_CLAMP_EXT:
- {
- /* s limited to [0,1] */
- /* i limited to [0,size-1] */
- const GLfloat u = FABSF(s);
- if (u <= 0.0F)
- i = 0;
- else if (u >= 1.0F)
- i = size - 1;
- else
- i = IFLOOR(u * size);
- }
- return i;
- case GL_MIRROR_CLAMP_TO_EDGE_EXT:
- {
- /* s limited to [min,max] */
- /* i limited to [0, size-1] */
- const GLfloat min = 1.0F / (2.0F * size);
- const GLfloat max = 1.0F - min;
- const GLfloat u = FABSF(s);
- if (u < min)
- i = 0;
- else if (u > max)
- i = size - 1;
- else
- i = IFLOOR(u * size);
- }
- return i;
- case GL_MIRROR_CLAMP_TO_BORDER_EXT:
- {
- /* s limited to [min,max] */
- /* i limited to [0, size-1] */
- const GLfloat min = -1.0F / (2.0F * size);
- const GLfloat max = 1.0F - min;
- const GLfloat u = FABSF(s);
- if (u < min)
- i = -1;
- else if (u > max)
- i = size;
- else
- i = IFLOOR(u * size);
- }
- return i;
- case GL_CLAMP:
- /* s limited to [0,1] */
- /* i limited to [0,size-1] */
- if (s <= 0.0F)
- i = 0;
- else if (s >= 1.0F)
- i = size - 1;
- else
- i = IFLOOR(s * size);
- return i;
- default:
- _mesa_problem(NULL, "Bad wrap mode");
- return 0;
- }
-}
-
-
-/* Power of two image sizes only */
-static INLINE void
-linear_repeat_texel_location(GLuint size, GLfloat s,
- GLint *i0, GLint *i1, GLfloat *weight)
-{
- GLfloat u = s * size - 0.5F;
- *i0 = IFLOOR(u) & (size - 1);
- *i1 = (*i0 + 1) & (size - 1);
- *weight = FRAC(u);
-}
-
-
-/**
- * Do clamp/wrap for a texture rectangle coord, GL_NEAREST filter mode.
- */
-static INLINE GLint
-clamp_rect_coord_nearest(GLenum wrapMode, GLfloat coord, GLint max)
-{
- switch (wrapMode) {
- case GL_CLAMP:
- return IFLOOR( CLAMP(coord, 0.0F, max - 1) );
- case GL_CLAMP_TO_EDGE:
- return IFLOOR( CLAMP(coord, 0.5F, max - 0.5F) );
- case GL_CLAMP_TO_BORDER:
- return IFLOOR( CLAMP(coord, -0.5F, max + 0.5F) );
- default:
- _mesa_problem(NULL, "bad wrapMode in clamp_rect_coord_nearest");
- return 0;
- }
-}
-
-
-/**
- * As above, but GL_LINEAR filtering.
- */
-static INLINE void
-clamp_rect_coord_linear(GLenum wrapMode, GLfloat coord, GLint max,
- GLint *i0out, GLint *i1out, GLfloat *weight)
-{
- GLfloat fcol;
- GLint i0, i1;
- switch (wrapMode) {
- case GL_CLAMP:
- /* Not exactly what the spec says, but it matches NVIDIA output */
- fcol = CLAMP(coord - 0.5F, 0.0F, max - 1);
- i0 = IFLOOR(fcol);
- i1 = i0 + 1;
- break;
- case GL_CLAMP_TO_EDGE:
- fcol = CLAMP(coord, 0.5F, max - 0.5F);
- fcol -= 0.5F;
- i0 = IFLOOR(fcol);
- i1 = i0 + 1;
- if (i1 > max - 1)
- i1 = max - 1;
- break;
- case GL_CLAMP_TO_BORDER:
- fcol = CLAMP(coord, -0.5F, max + 0.5F);
- fcol -= 0.5F;
- i0 = IFLOOR(fcol);
- i1 = i0 + 1;
- break;
- default:
- _mesa_problem(NULL, "bad wrapMode in clamp_rect_coord_linear");
- i0 = i1 = 0;
- fcol = 0.0F;
- }
- *i0out = i0;
- *i1out = i1;
- *weight = FRAC(fcol);
-}
-
-
-/**
- * Compute slice/image to use for 1D or 2D array texture.
- */
-static INLINE GLint
-tex_array_slice(GLfloat coord, GLsizei size)
-{
- GLint slice = IFLOOR(coord + 0.5f);
- slice = CLAMP(slice, 0, size - 1);
- return slice;
-}
-
-
-/**
- * Compute nearest integer texcoords for given texobj and coordinate.
- * NOTE: only used for depth texture sampling.
- */
-static INLINE void
-nearest_texcoord(const struct gl_texture_object *texObj,
- GLuint level,
- const GLfloat texcoord[4],
- GLint *i, GLint *j, GLint *k)
-{
- const struct gl_texture_image *img = texObj->Image[0][level];
- const GLint width = img->Width;
- const GLint height = img->Height;
- const GLint depth = img->Depth;
-
- switch (texObj->Target) {
- case GL_TEXTURE_RECTANGLE_ARB:
- *i = clamp_rect_coord_nearest(texObj->Sampler.WrapS, texcoord[0], width);
- *j = clamp_rect_coord_nearest(texObj->Sampler.WrapT, texcoord[1], height);
- *k = 0;
- break;
- case GL_TEXTURE_1D:
- *i = nearest_texel_location(texObj->Sampler.WrapS, img, width, texcoord[0]);
- *j = 0;
- *k = 0;
- break;
- case GL_TEXTURE_2D:
- *i = nearest_texel_location(texObj->Sampler.WrapS, img, width, texcoord[0]);
- *j = nearest_texel_location(texObj->Sampler.WrapT, img, height, texcoord[1]);
- *k = 0;
- break;
- case GL_TEXTURE_1D_ARRAY_EXT:
- *i = nearest_texel_location(texObj->Sampler.WrapS, img, width, texcoord[0]);
- *j = tex_array_slice(texcoord[1], height);
- *k = 0;
- break;
- case GL_TEXTURE_2D_ARRAY_EXT:
- *i = nearest_texel_location(texObj->Sampler.WrapS, img, width, texcoord[0]);
- *j = nearest_texel_location(texObj->Sampler.WrapT, img, height, texcoord[1]);
- *k = tex_array_slice(texcoord[2], depth);
- break;
- default:
- *i = *j = *k = 0;
- }
-}
-
-
-/**
- * Compute linear integer texcoords for given texobj and coordinate.
- * NOTE: only used for depth texture sampling.
- */
-static INLINE void
-linear_texcoord(const struct gl_texture_object *texObj,
- GLuint level,
- const GLfloat texcoord[4],
- GLint *i0, GLint *i1, GLint *j0, GLint *j1, GLint *slice,
- GLfloat *wi, GLfloat *wj)
-{
- const struct gl_texture_image *img = texObj->Image[0][level];
- const GLint width = img->Width;
- const GLint height = img->Height;
- const GLint depth = img->Depth;
-
- switch (texObj->Target) {
- case GL_TEXTURE_RECTANGLE_ARB:
- clamp_rect_coord_linear(texObj->Sampler.WrapS, texcoord[0],
- width, i0, i1, wi);
- clamp_rect_coord_linear(texObj->Sampler.WrapT, texcoord[1],
- height, j0, j1, wj);
- *slice = 0;
- break;
-
- case GL_TEXTURE_1D:
- case GL_TEXTURE_2D:
- linear_texel_locations(texObj->Sampler.WrapS, img, width,
- texcoord[0], i0, i1, wi);
- linear_texel_locations(texObj->Sampler.WrapT, img, height,
- texcoord[1], j0, j1, wj);
- *slice = 0;
- break;
-
- case GL_TEXTURE_1D_ARRAY_EXT:
- linear_texel_locations(texObj->Sampler.WrapS, img, width,
- texcoord[0], i0, i1, wi);
- *j0 = tex_array_slice(texcoord[1], height);
- *j1 = *j0;
- *slice = 0;
- break;
-
- case GL_TEXTURE_2D_ARRAY_EXT:
- linear_texel_locations(texObj->Sampler.WrapS, img, width,
- texcoord[0], i0, i1, wi);
- linear_texel_locations(texObj->Sampler.WrapT, img, height,
- texcoord[1], j0, j1, wj);
- *slice = tex_array_slice(texcoord[2], depth);
- break;
-
- default:
- *slice = 0;
- }
-}
-
-
-
-/**
- * For linear interpolation between mipmap levels N and N+1, this function
- * computes N.
- */
-static INLINE GLint
-linear_mipmap_level(const struct gl_texture_object *tObj, GLfloat lambda)
-{
- if (lambda < 0.0F)
- return tObj->BaseLevel;
- else if (lambda > tObj->_MaxLambda)
- return (GLint) (tObj->BaseLevel + tObj->_MaxLambda);
- else
- return (GLint) (tObj->BaseLevel + lambda);
-}
-
-
-/**
- * Compute the nearest mipmap level to take texels from.
- */
-static INLINE GLint
-nearest_mipmap_level(const struct gl_texture_object *tObj, GLfloat lambda)
-{
- GLfloat l;
- GLint level;
- if (lambda <= 0.5F)
- l = 0.0F;
- else if (lambda > tObj->_MaxLambda + 0.4999F)
- l = tObj->_MaxLambda + 0.4999F;
- else
- l = lambda;
- level = (GLint) (tObj->BaseLevel + l + 0.5F);
- if (level > tObj->_MaxLevel)
- level = tObj->_MaxLevel;
- return level;
-}
-
-
-
-/*
- * Bitflags for texture border color sampling.
- */
-#define I0BIT 1
-#define I1BIT 2
-#define J0BIT 4
-#define J1BIT 8
-#define K0BIT 16
-#define K1BIT 32
-
-
-
-/**
- * The lambda[] array values are always monotonic. Either the whole span
- * will be minified, magnified, or split between the two. This function
- * determines the subranges in [0, n-1] that are to be minified or magnified.
- */
-static INLINE void
-compute_min_mag_ranges(const struct gl_texture_object *tObj,
- GLuint n, const GLfloat lambda[],
- GLuint *minStart, GLuint *minEnd,
- GLuint *magStart, GLuint *magEnd)
-{
- GLfloat minMagThresh;
-
- /* we shouldn't be here if minfilter == magfilter */
- ASSERT(tObj->Sampler.MinFilter != tObj->Sampler.MagFilter);
-
- /* This bit comes from the OpenGL spec: */
- if (tObj->Sampler.MagFilter == GL_LINEAR
- && (tObj->Sampler.MinFilter == GL_NEAREST_MIPMAP_NEAREST ||
- tObj->Sampler.MinFilter == GL_NEAREST_MIPMAP_LINEAR)) {
- minMagThresh = 0.5F;
- }
- else {
- minMagThresh = 0.0F;
- }
-
-#if 0
- /* DEBUG CODE: Verify that lambda[] is monotonic.
- * We can't really use this because the inaccuracy in the LOG2 function
- * causes this test to fail, yet the resulting texturing is correct.
- */
- if (n > 1) {
- GLuint i;
- printf("lambda delta = %g\n", lambda[0] - lambda[n-1]);
- if (lambda[0] >= lambda[n-1]) { /* decreasing */
- for (i = 0; i < n - 1; i++) {
- ASSERT((GLint) (lambda[i] * 10) >= (GLint) (lambda[i+1] * 10));
- }
- }
- else { /* increasing */
- for (i = 0; i < n - 1; i++) {
- ASSERT((GLint) (lambda[i] * 10) <= (GLint) (lambda[i+1] * 10));
- }
- }
- }
-#endif /* DEBUG */
-
- if (lambda[0] <= minMagThresh && (n <= 1 || lambda[n-1] <= minMagThresh)) {
- /* magnification for whole span */
- *magStart = 0;
- *magEnd = n;
- *minStart = *minEnd = 0;
- }
- else if (lambda[0] > minMagThresh && (n <=1 || lambda[n-1] > minMagThresh)) {
- /* minification for whole span */
- *minStart = 0;
- *minEnd = n;
- *magStart = *magEnd = 0;
- }
- else {
- /* a mix of minification and magnification */
- GLuint i;
- if (lambda[0] > minMagThresh) {
- /* start with minification */
- for (i = 1; i < n; i++) {
- if (lambda[i] <= minMagThresh)
- break;
- }
- *minStart = 0;
- *minEnd = i;
- *magStart = i;
- *magEnd = n;
- }
- else {
- /* start with magnification */
- for (i = 1; i < n; i++) {
- if (lambda[i] > minMagThresh)
- break;
- }
- *magStart = 0;
- *magEnd = i;
- *minStart = i;
- *minEnd = n;
- }
- }
-
-#if 0
- /* Verify the min/mag Start/End values
- * We don't use this either (see above)
- */
- {
- GLint i;
- for (i = 0; i < n; i++) {
- if (lambda[i] > minMagThresh) {
- /* minification */
- ASSERT(i >= *minStart);
- ASSERT(i < *minEnd);
- }
- else {
- /* magnification */
- ASSERT(i >= *magStart);
- ASSERT(i < *magEnd);
- }
- }
- }
-#endif
-}
-
-
-/**
- * When we sample the border color, it must be interpreted according to
- * the base texture format. Ex: if the texture base format it GL_ALPHA,
- * we return (0,0,0,BorderAlpha).
- */
-static INLINE void
-get_border_color(const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- GLfloat rgba[4])
-{
- switch (img->_BaseFormat) {
- case GL_RGB:
- rgba[0] = tObj->Sampler.BorderColor.f[0];
- rgba[1] = tObj->Sampler.BorderColor.f[1];
- rgba[2] = tObj->Sampler.BorderColor.f[2];
- rgba[3] = 1.0F;
- break;
- case GL_ALPHA:
- rgba[0] = rgba[1] = rgba[2] = 0.0;
- rgba[3] = tObj->Sampler.BorderColor.f[3];
- break;
- case GL_LUMINANCE:
- rgba[0] = rgba[1] = rgba[2] = tObj->Sampler.BorderColor.f[0];
- rgba[3] = 1.0;
- break;
- case GL_LUMINANCE_ALPHA:
- rgba[0] = rgba[1] = rgba[2] = tObj->Sampler.BorderColor.f[0];
- rgba[3] = tObj->Sampler.BorderColor.f[3];
- break;
- case GL_INTENSITY:
- rgba[0] = rgba[1] = rgba[2] = rgba[3] = tObj->Sampler.BorderColor.f[0];
- break;
- default:
- COPY_4V(rgba, tObj->Sampler.BorderColor.f);
- }
-}
-
-
-/**********************************************************************/
-/* 1-D Texture Sampling Functions */
-/**********************************************************************/
-
-/**
- * Return the texture sample for coordinate (s) using GL_NEAREST filter.
- */
-static INLINE void
-sample_1d_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4], GLfloat rgba[4])
-{
- const GLint width = img->Width2; /* without border, power of two */
- GLint i;
- i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]);
- /* skip over the border, if any */
- i += img->Border;
- if (i < 0 || i >= (GLint) img->Width) {
- /* Need this test for GL_CLAMP_TO_BORDER mode */
- get_border_color(tObj, img, rgba);
- }
- else {
- img->FetchTexelf(img, i, 0, 0, rgba);
- }
-}
-
-
-/**
- * Return the texture sample for coordinate (s) using GL_LINEAR filter.
- */
-static INLINE void
-sample_1d_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4], GLfloat rgba[4])
-{
- const GLint width = img->Width2;
- GLint i0, i1;
- GLbitfield useBorderColor = 0x0;
- GLfloat a;
- GLfloat t0[4], t1[4]; /* texels */
-
- linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a);
-
- if (img->Border) {
- i0 += img->Border;
- i1 += img->Border;
- }
- else {
- if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT;
- if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT;
- }
-
- /* fetch texel colors */
- if (useBorderColor & I0BIT) {
- get_border_color(tObj, img, t0);
- }
- else {
- img->FetchTexelf(img, i0, 0, 0, t0);
- }
- if (useBorderColor & I1BIT) {
- get_border_color(tObj, img, t1);
- }
- else {
- img->FetchTexelf(img, i1, 0, 0, t1);
- }
-
- lerp_rgba(rgba, a, t0, t1);
-}
-
-
-static void
-sample_1d_nearest_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_1d_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]);
- }
-}
-
-
-static void
-sample_1d_linear_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_1d_linear(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]);
- }
-}
-
-
-static void
-sample_1d_nearest_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_1d_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4];
- const GLfloat f = FRAC(lambda[i]);
- sample_1d_nearest(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0);
- sample_1d_nearest(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-static void
-sample_1d_linear_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_1d_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4];
- const GLfloat f = FRAC(lambda[i]);
- sample_1d_linear(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0);
- sample_1d_linear(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-/** Sample 1D texture, nearest filtering for both min/magnification */
-static void
-sample_nearest_1d( struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4] )
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- for (i = 0; i < n; i++) {
- sample_1d_nearest(ctx, tObj, image, texcoords[i], rgba[i]);
- }
-}
-
-
-/** Sample 1D texture, linear filtering for both min/magnification */
-static void
-sample_linear_1d( struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4] )
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- for (i = 0; i < n; i++) {
- sample_1d_linear(ctx, tObj, image, texcoords[i], rgba[i]);
- }
-}
-
-
-/** Sample 1D texture, using lambda to choose between min/magnification */
-static void
-sample_lambda_1d( struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4] )
-{
- GLuint minStart, minEnd; /* texels with minification */
- GLuint magStart, magEnd; /* texels with magnification */
- GLuint i;
-
- ASSERT(lambda != NULL);
- compute_min_mag_ranges(tObj, n, lambda,
- &minStart, &minEnd, &magStart, &magEnd);
-
- if (minStart < minEnd) {
- /* do the minified texels */
- const GLuint m = minEnd - minStart;
- switch (tObj->Sampler.MinFilter) {
- case GL_NEAREST:
- for (i = minStart; i < minEnd; i++)
- sample_1d_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_LINEAR:
- for (i = minStart; i < minEnd; i++)
- sample_1d_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_NEAREST_MIPMAP_NEAREST:
- sample_1d_nearest_mipmap_nearest(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_NEAREST:
- sample_1d_linear_mipmap_nearest(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_NEAREST_MIPMAP_LINEAR:
- sample_1d_nearest_mipmap_linear(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_LINEAR:
- sample_1d_linear_mipmap_linear(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- default:
- _mesa_problem(ctx, "Bad min filter in sample_1d_texture");
- return;
- }
- }
-
- if (magStart < magEnd) {
- /* do the magnified texels */
- switch (tObj->Sampler.MagFilter) {
- case GL_NEAREST:
- for (i = magStart; i < magEnd; i++)
- sample_1d_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_LINEAR:
- for (i = magStart; i < magEnd; i++)
- sample_1d_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- default:
- _mesa_problem(ctx, "Bad mag filter in sample_1d_texture");
- return;
- }
- }
-}
-
-
-/**********************************************************************/
-/* 2-D Texture Sampling Functions */
-/**********************************************************************/
-
-
-/**
- * Return the texture sample for coordinate (s,t) using GL_NEAREST filter.
- */
-static INLINE void
-sample_2d_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4],
- GLfloat rgba[])
-{
- const GLint width = img->Width2; /* without border, power of two */
- const GLint height = img->Height2; /* without border, power of two */
- GLint i, j;
- (void) ctx;
-
- i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]);
- j = nearest_texel_location(tObj->Sampler.WrapT, img, height, texcoord[1]);
-
- /* skip over the border, if any */
- i += img->Border;
- j += img->Border;
-
- if (i < 0 || i >= (GLint) img->Width || j < 0 || j >= (GLint) img->Height) {
- /* Need this test for GL_CLAMP_TO_BORDER mode */
- get_border_color(tObj, img, rgba);
- }
- else {
- img->FetchTexelf(img, i, j, 0, rgba);
- }
-}
-
-
-/**
- * Return the texture sample for coordinate (s,t) using GL_LINEAR filter.
- * New sampling code contributed by Lynn Quam <quam@ai.sri.com>.
- */
-static INLINE void
-sample_2d_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4],
- GLfloat rgba[])
-{
- const GLint width = img->Width2;
- const GLint height = img->Height2;
- GLint i0, j0, i1, j1;
- GLbitfield useBorderColor = 0x0;
- GLfloat a, b;
- GLfloat t00[4], t10[4], t01[4], t11[4]; /* sampled texel colors */
-
- linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a);
- linear_texel_locations(tObj->Sampler.WrapT, img, height, texcoord[1], &j0, &j1, &b);
-
- if (img->Border) {
- i0 += img->Border;
- i1 += img->Border;
- j0 += img->Border;
- j1 += img->Border;
- }
- else {
- if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT;
- if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT;
- if (j0 < 0 || j0 >= height) useBorderColor |= J0BIT;
- if (j1 < 0 || j1 >= height) useBorderColor |= J1BIT;
- }
-
- /* fetch four texel colors */
- if (useBorderColor & (I0BIT | J0BIT)) {
- get_border_color(tObj, img, t00);
- }
- else {
- img->FetchTexelf(img, i0, j0, 0, t00);
- }
- if (useBorderColor & (I1BIT | J0BIT)) {
- get_border_color(tObj, img, t10);
- }
- else {
- img->FetchTexelf(img, i1, j0, 0, t10);
- }
- if (useBorderColor & (I0BIT | J1BIT)) {
- get_border_color(tObj, img, t01);
- }
- else {
- img->FetchTexelf(img, i0, j1, 0, t01);
- }
- if (useBorderColor & (I1BIT | J1BIT)) {
- get_border_color(tObj, img, t11);
- }
- else {
- img->FetchTexelf(img, i1, j1, 0, t11);
- }
-
- lerp_rgba_2d(rgba, a, b, t00, t10, t01, t11);
-}
-
-
-/**
- * As above, but we know WRAP_S == REPEAT and WRAP_T == REPEAT.
- * We don't have to worry about the texture border.
- */
-static INLINE void
-sample_2d_linear_repeat(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4],
- GLfloat rgba[])
-{
- const GLint width = img->Width2;
- const GLint height = img->Height2;
- GLint i0, j0, i1, j1;
- GLfloat wi, wj;
- GLfloat t00[4], t10[4], t01[4], t11[4]; /* sampled texel colors */
-
- (void) ctx;
-
- ASSERT(tObj->Sampler.WrapS == GL_REPEAT);
- ASSERT(tObj->Sampler.WrapT == GL_REPEAT);
- ASSERT(img->Border == 0);
- ASSERT(img->_BaseFormat != GL_COLOR_INDEX);
- ASSERT(img->_IsPowerOfTwo);
-
- linear_repeat_texel_location(width, texcoord[0], &i0, &i1, &wi);
- linear_repeat_texel_location(height, texcoord[1], &j0, &j1, &wj);
-
- img->FetchTexelf(img, i0, j0, 0, t00);
- img->FetchTexelf(img, i1, j0, 0, t10);
- img->FetchTexelf(img, i0, j1, 0, t01);
- img->FetchTexelf(img, i1, j1, 0, t11);
-
- lerp_rgba_2d(rgba, wi, wj, t00, t10, t01, t11);
-}
-
-
-static void
-sample_2d_nearest_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_2d_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]);
- }
-}
-
-
-static void
-sample_2d_linear_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_2d_linear(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]);
- }
-}
-
-
-static void
-sample_2d_nearest_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_2d_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_2d_nearest(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0);
- sample_2d_nearest(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-static void
-sample_2d_linear_mipmap_linear( struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4] )
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_2d_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_2d_linear(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0);
- sample_2d_linear(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-static void
-sample_2d_linear_mipmap_linear_repeat(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- ASSERT(tObj->Sampler.WrapS == GL_REPEAT);
- ASSERT(tObj->Sampler.WrapT == GL_REPEAT);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_2d_linear_repeat(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_2d_linear_repeat(ctx, tObj, tObj->Image[0][level ],
- texcoord[i], t0);
- sample_2d_linear_repeat(ctx, tObj, tObj->Image[0][level+1],
- texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-/** Sample 2D texture, nearest filtering for both min/magnification */
-static void
-sample_nearest_2d(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- for (i = 0; i < n; i++) {
- sample_2d_nearest(ctx, tObj, image, texcoords[i], rgba[i]);
- }
-}
-
-
-/** Sample 2D texture, linear filtering for both min/magnification */
-static void
-sample_linear_2d(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- if (tObj->Sampler.WrapS == GL_REPEAT &&
- tObj->Sampler.WrapT == GL_REPEAT &&
- image->_IsPowerOfTwo &&
- image->Border == 0) {
- for (i = 0; i < n; i++) {
- sample_2d_linear_repeat(ctx, tObj, image, texcoords[i], rgba[i]);
- }
- }
- else {
- for (i = 0; i < n; i++) {
- sample_2d_linear(ctx, tObj, image, texcoords[i], rgba[i]);
- }
- }
-}
-
-
-/**
- * Optimized 2-D texture sampling:
- * S and T wrap mode == GL_REPEAT
- * GL_NEAREST min/mag filter
- * No border,
- * RowStride == Width,
- * Format = GL_RGB
- */
-static void
-opt_sample_rgb_2d(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- const struct gl_texture_image *img = tObj->Image[0][tObj->BaseLevel];
- const GLfloat width = (GLfloat) img->Width;
- const GLfloat height = (GLfloat) img->Height;
- const GLint colMask = img->Width - 1;
- const GLint rowMask = img->Height - 1;
- const GLint shift = img->WidthLog2;
- GLuint k;
- (void) ctx;
- (void) lambda;
- ASSERT(tObj->Sampler.WrapS==GL_REPEAT);
- ASSERT(tObj->Sampler.WrapT==GL_REPEAT);
- ASSERT(img->Border==0);
- ASSERT(img->TexFormat == MESA_FORMAT_RGB888);
- ASSERT(img->_IsPowerOfTwo);
-
- for (k=0; k<n; k++) {
- GLint i = IFLOOR(texcoords[k][0] * width) & colMask;
- GLint j = IFLOOR(texcoords[k][1] * height) & rowMask;
- GLint pos = (j << shift) | i;
- GLubyte *texel = ((GLubyte *) img->Data) + 3*pos;
- rgba[k][RCOMP] = UBYTE_TO_FLOAT(texel[2]);
- rgba[k][GCOMP] = UBYTE_TO_FLOAT(texel[1]);
- rgba[k][BCOMP] = UBYTE_TO_FLOAT(texel[0]);
- rgba[k][ACOMP] = 1.0F;
- }
-}
-
-
-/**
- * Optimized 2-D texture sampling:
- * S and T wrap mode == GL_REPEAT
- * GL_NEAREST min/mag filter
- * No border
- * RowStride == Width,
- * Format = GL_RGBA
- */
-static void
-opt_sample_rgba_2d(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- const struct gl_texture_image *img = tObj->Image[0][tObj->BaseLevel];
- const GLfloat width = (GLfloat) img->Width;
- const GLfloat height = (GLfloat) img->Height;
- const GLint colMask = img->Width - 1;
- const GLint rowMask = img->Height - 1;
- const GLint shift = img->WidthLog2;
- GLuint i;
- (void) ctx;
- (void) lambda;
- ASSERT(tObj->Sampler.WrapS==GL_REPEAT);
- ASSERT(tObj->Sampler.WrapT==GL_REPEAT);
- ASSERT(img->Border==0);
- ASSERT(img->TexFormat == MESA_FORMAT_RGBA8888);
- ASSERT(img->_IsPowerOfTwo);
-
- for (i = 0; i < n; i++) {
- const GLint col = IFLOOR(texcoords[i][0] * width) & colMask;
- const GLint row = IFLOOR(texcoords[i][1] * height) & rowMask;
- const GLint pos = (row << shift) | col;
- const GLuint texel = *((GLuint *) img->Data + pos);
- rgba[i][RCOMP] = UBYTE_TO_FLOAT( (texel >> 24) );
- rgba[i][GCOMP] = UBYTE_TO_FLOAT( (texel >> 16) & 0xff );
- rgba[i][BCOMP] = UBYTE_TO_FLOAT( (texel >> 8) & 0xff );
- rgba[i][ACOMP] = UBYTE_TO_FLOAT( (texel ) & 0xff );
- }
-}
-
-
-/** Sample 2D texture, using lambda to choose between min/magnification */
-static void
-sample_lambda_2d(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- const struct gl_texture_image *tImg = tObj->Image[0][tObj->BaseLevel];
- GLuint minStart, minEnd; /* texels with minification */
- GLuint magStart, magEnd; /* texels with magnification */
-
- const GLboolean repeatNoBorderPOT = (tObj->Sampler.WrapS == GL_REPEAT)
- && (tObj->Sampler.WrapT == GL_REPEAT)
- && (tImg->Border == 0 && (tImg->Width == tImg->RowStride))
- && (tImg->_BaseFormat != GL_COLOR_INDEX)
- && tImg->_IsPowerOfTwo;
-
- ASSERT(lambda != NULL);
- compute_min_mag_ranges(tObj, n, lambda,
- &minStart, &minEnd, &magStart, &magEnd);
-
- if (minStart < minEnd) {
- /* do the minified texels */
- const GLuint m = minEnd - minStart;
- switch (tObj->Sampler.MinFilter) {
- case GL_NEAREST:
- if (repeatNoBorderPOT) {
- switch (tImg->TexFormat) {
- case MESA_FORMAT_RGB888:
- opt_sample_rgb_2d(ctx, tObj, m, texcoords + minStart,
- NULL, rgba + minStart);
- break;
- case MESA_FORMAT_RGBA8888:
- opt_sample_rgba_2d(ctx, tObj, m, texcoords + minStart,
- NULL, rgba + minStart);
- break;
- default:
- sample_nearest_2d(ctx, tObj, m, texcoords + minStart,
- NULL, rgba + minStart );
- }
- }
- else {
- sample_nearest_2d(ctx, tObj, m, texcoords + minStart,
- NULL, rgba + minStart);
- }
- break;
- case GL_LINEAR:
- sample_linear_2d(ctx, tObj, m, texcoords + minStart,
- NULL, rgba + minStart);
- break;
- case GL_NEAREST_MIPMAP_NEAREST:
- sample_2d_nearest_mipmap_nearest(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_NEAREST:
- sample_2d_linear_mipmap_nearest(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_NEAREST_MIPMAP_LINEAR:
- sample_2d_nearest_mipmap_linear(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_LINEAR:
- if (repeatNoBorderPOT)
- sample_2d_linear_mipmap_linear_repeat(ctx, tObj, m,
- texcoords + minStart, lambda + minStart, rgba + minStart);
- else
- sample_2d_linear_mipmap_linear(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- default:
- _mesa_problem(ctx, "Bad min filter in sample_2d_texture");
- return;
- }
- }
-
- if (magStart < magEnd) {
- /* do the magnified texels */
- const GLuint m = magEnd - magStart;
-
- switch (tObj->Sampler.MagFilter) {
- case GL_NEAREST:
- if (repeatNoBorderPOT) {
- switch (tImg->TexFormat) {
- case MESA_FORMAT_RGB888:
- opt_sample_rgb_2d(ctx, tObj, m, texcoords + magStart,
- NULL, rgba + magStart);
- break;
- case MESA_FORMAT_RGBA8888:
- opt_sample_rgba_2d(ctx, tObj, m, texcoords + magStart,
- NULL, rgba + magStart);
- break;
- default:
- sample_nearest_2d(ctx, tObj, m, texcoords + magStart,
- NULL, rgba + magStart );
- }
- }
- else {
- sample_nearest_2d(ctx, tObj, m, texcoords + magStart,
- NULL, rgba + magStart);
- }
- break;
- case GL_LINEAR:
- sample_linear_2d(ctx, tObj, m, texcoords + magStart,
- NULL, rgba + magStart);
- break;
- default:
- _mesa_problem(ctx, "Bad mag filter in sample_lambda_2d");
- }
- }
-}
-
-
-/* For anisotropic filtering */
-#define WEIGHT_LUT_SIZE 1024
-
-static GLfloat *weightLut = NULL;
-
-/**
- * Creates the look-up table used to speed-up EWA sampling
- */
-static void
-create_filter_table(void)
-{
- GLuint i;
- if (!weightLut) {
- weightLut = (GLfloat *) malloc(WEIGHT_LUT_SIZE * sizeof(GLfloat));
-
- for (i = 0; i < WEIGHT_LUT_SIZE; ++i) {
- GLfloat alpha = 2;
- GLfloat r2 = (GLfloat) i / (GLfloat) (WEIGHT_LUT_SIZE - 1);
- GLfloat weight = (GLfloat) exp(-alpha * r2);
- weightLut[i] = weight;
- }
- }
-}
-
-
-/**
- * Elliptical weighted average (EWA) filter for producing high quality
- * anisotropic filtered results.
- * Based on the Higher Quality Elliptical Weighted Avarage Filter
- * published by Paul S. Heckbert in his Master's Thesis
- * "Fundamentals of Texture Mapping and Image Warping" (1989)
- */
-static void
-sample_2d_ewa(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const GLfloat texcoord[4],
- const GLfloat dudx, const GLfloat dvdx,
- const GLfloat dudy, const GLfloat dvdy, const GLint lod,
- GLfloat rgba[])
-{
- GLint level = lod > 0 ? lod : 0;
- GLfloat scaling = 1.0 / (1 << level);
- const struct gl_texture_image *img = tObj->Image[0][level];
- const struct gl_texture_image *mostDetailedImage =
- tObj->Image[0][tObj->BaseLevel];
- GLfloat tex_u=-0.5 + texcoord[0] * mostDetailedImage->WidthScale * scaling;
- GLfloat tex_v=-0.5 + texcoord[1] * mostDetailedImage->HeightScale * scaling;
-
- GLfloat ux = dudx * scaling;
- GLfloat vx = dvdx * scaling;
- GLfloat uy = dudy * scaling;
- GLfloat vy = dvdy * scaling;
-
- /* compute ellipse coefficients to bound the region:
- * A*x*x + B*x*y + C*y*y = F.
- */
- GLfloat A = vx*vx+vy*vy+1;
- GLfloat B = -2*(ux*vx+uy*vy);
- GLfloat C = ux*ux+uy*uy+1;
- GLfloat F = A*C-B*B/4.0;
-
- /* check if it is an ellipse */
- /* ASSERT(F > 0.0); */
-
- /* Compute the ellipse's (u,v) bounding box in texture space */
- GLfloat d = -B*B+4.0*C*A;
- GLfloat box_u = 2.0 / d * sqrt(d*C*F); /* box_u -> half of bbox with */
- GLfloat box_v = 2.0 / d * sqrt(A*d*F); /* box_v -> half of bbox height */
-
- GLint u0 = floor(tex_u - box_u);
- GLint u1 = ceil (tex_u + box_u);
- GLint v0 = floor(tex_v - box_v);
- GLint v1 = ceil (tex_v + box_v);
-
- GLfloat num[4] = {0.0F, 0.0F, 0.0F, 0.0F};
- GLfloat newCoord[2];
- GLfloat den = 0.0F;
- GLfloat ddq;
- GLfloat U = u0 - tex_u;
- GLint v;
-
- /* Scale ellipse formula to directly index the Filter Lookup Table.
- * i.e. scale so that F = WEIGHT_LUT_SIZE-1
- */
- double formScale = (double) (WEIGHT_LUT_SIZE - 1) / F;
- A *= formScale;
- B *= formScale;
- C *= formScale;
- /* F *= formScale; */ /* no need to scale F as we don't use it below here */
-
- /* Heckbert MS thesis, p. 59; scan over the bounding box of the ellipse
- * and incrementally update the value of Ax^2+Bxy*Cy^2; when this
- * value, q, is less than F, we're inside the ellipse
- */
- ddq = 2 * A;
- for (v = v0; v <= v1; ++v) {
- GLfloat V = v - tex_v;
- GLfloat dq = A * (2 * U + 1) + B * V;
- GLfloat q = (C * V + B * U) * V + A * U * U;
-
- GLint u;
- for (u = u0; u <= u1; ++u) {
- /* Note that the ellipse has been pre-scaled so F = WEIGHT_LUT_SIZE - 1 */
- if (q < WEIGHT_LUT_SIZE) {
- /* as a LUT is used, q must never be negative;
- * should not happen, though
- */
- const GLint qClamped = q >= 0.0F ? q : 0;
- GLfloat weight = weightLut[qClamped];
-
- newCoord[0] = u / ((GLfloat) img->Width2);
- newCoord[1] = v / ((GLfloat) img->Height2);
-
- sample_2d_nearest(ctx, tObj, img, newCoord, rgba);
- num[0] += weight * rgba[0];
- num[1] += weight * rgba[1];
- num[2] += weight * rgba[2];
- num[3] += weight * rgba[3];
-
- den += weight;
- }
- q += dq;
- dq += ddq;
- }
- }
-
- if (den <= 0.0F) {
- /* Reaching this place would mean
- * that no pixels intersected the ellipse.
- * This should never happen because
- * the filter we use always
- * intersects at least one pixel.
- */
-
- /*rgba[0]=0;
- rgba[1]=0;
- rgba[2]=0;
- rgba[3]=0;*/
- /* not enough pixels in resampling, resort to direct interpolation */
- sample_2d_linear(ctx, tObj, img, texcoord, rgba);
- return;
- }
-
- rgba[0] = num[0] / den;
- rgba[1] = num[1] / den;
- rgba[2] = num[2] / den;
- rgba[3] = num[3] / den;
-}
-
-
-/**
- * Anisotropic filtering using footprint assembly as outlined in the
- * EXT_texture_filter_anisotropic spec:
- * http://www.opengl.org/registry/specs/EXT/texture_filter_anisotropic.txt
- * Faster than EWA but has less quality (more aliasing effects)
- */
-static void
-sample_2d_footprint(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const GLfloat texcoord[4],
- const GLfloat dudx, const GLfloat dvdx,
- const GLfloat dudy, const GLfloat dvdy, const GLint lod,
- GLfloat rgba[])
-{
- GLint level = lod > 0 ? lod : 0;
- GLfloat scaling = 1.0F / (1 << level);
- const struct gl_texture_image *img = tObj->Image[0][level];
-
- GLfloat ux = dudx * scaling;
- GLfloat vx = dvdx * scaling;
- GLfloat uy = dudy * scaling;
- GLfloat vy = dvdy * scaling;
-
- GLfloat Px2 = ux * ux + vx * vx; /* squared length of dx */
- GLfloat Py2 = uy * uy + vy * vy; /* squared length of dy */
-
- GLint numSamples;
- GLfloat ds;
- GLfloat dt;
-
- GLfloat num[4] = {0.0F, 0.0F, 0.0F, 0.0F};
- GLfloat newCoord[2];
- GLint s;
-
- /* Calculate the per anisotropic sample offsets in s,t space. */
- if (Px2 > Py2) {
- numSamples = ceil(SQRTF(Px2));
- ds = ux / ((GLfloat) img->Width2);
- dt = vx / ((GLfloat) img->Height2);
- }
- else {
- numSamples = ceil(SQRTF(Py2));
- ds = uy / ((GLfloat) img->Width2);
- dt = vy / ((GLfloat) img->Height2);
- }
-
- for (s = 0; s<numSamples; s++) {
- newCoord[0] = texcoord[0] + ds * ((GLfloat)(s+1) / (numSamples+1) -0.5);
- newCoord[1] = texcoord[1] + dt * ((GLfloat)(s+1) / (numSamples+1) -0.5);
-
- sample_2d_linear(ctx, tObj, img, newCoord, rgba);
- num[0] += rgba[0];
- num[1] += rgba[1];
- num[2] += rgba[2];
- num[3] += rgba[3];
- }
-
- rgba[0] = num[0] / numSamples;
- rgba[1] = num[1] / numSamples;
- rgba[2] = num[2] / numSamples;
- rgba[3] = num[3] / numSamples;
-}
-
-
-/**
- * Returns the index of the specified texture object in the
- * gl_context texture unit array.
- */
-static INLINE GLuint
-texture_unit_index(const struct gl_context *ctx,
- const struct gl_texture_object *tObj)
-{
- const GLuint maxUnit
- = (ctx->Texture._EnabledCoordUnits > 1) ? ctx->Const.MaxTextureUnits : 1;
- GLuint u;
-
- /* XXX CoordUnits vs. ImageUnits */
- for (u = 0; u < maxUnit; u++) {
- if (ctx->Texture.Unit[u]._Current == tObj)
- break; /* found */
- }
- if (u >= maxUnit)
- u = 0; /* not found, use 1st one; should never happen */
-
- return u;
-}
-
-
-/**
- * Sample 2D texture using an anisotropic filter.
- * NOTE: the const GLfloat lambda_iso[] parameter does *NOT* contain
- * the lambda float array but a "hidden" SWspan struct which is required
- * by this function but is not available in the texture_sample_func signature.
- * See _swrast_texture_span( struct gl_context *ctx, SWspan *span ) on how
- * this function is called.
- */
-static void
-sample_lambda_2d_aniso(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoords[][4],
- const GLfloat lambda_iso[], GLfloat rgba[][4])
-{
- const struct gl_texture_image *tImg = tObj->Image[0][tObj->BaseLevel];
- const GLfloat maxEccentricity =
- tObj->Sampler.MaxAnisotropy * tObj->Sampler.MaxAnisotropy;
-
- /* re-calculate the lambda values so that they are usable with anisotropic
- * filtering
- */
- SWspan *span = (SWspan *)lambda_iso; /* access the "hidden" SWspan struct */
-
- /* based on interpolate_texcoords(struct gl_context *ctx, SWspan *span)
- * in swrast/s_span.c
- */
-
- /* find the texture unit index by looking up the current texture object
- * from the context list of available texture objects.
- */
- const GLuint u = texture_unit_index(ctx, tObj);
- const GLuint attr = FRAG_ATTRIB_TEX0 + u;
- GLfloat texW, texH;
-
- const GLfloat dsdx = span->attrStepX[attr][0];
- const GLfloat dsdy = span->attrStepY[attr][0];
- const GLfloat dtdx = span->attrStepX[attr][1];
- const GLfloat dtdy = span->attrStepY[attr][1];
- const GLfloat dqdx = span->attrStepX[attr][3];
- const GLfloat dqdy = span->attrStepY[attr][3];
- GLfloat s = span->attrStart[attr][0] + span->leftClip * dsdx;
- GLfloat t = span->attrStart[attr][1] + span->leftClip * dtdx;
- GLfloat q = span->attrStart[attr][3] + span->leftClip * dqdx;
-
- /* from swrast/s_texcombine.c _swrast_texture_span */
- const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[u];
- const GLboolean adjustLOD =
- (texUnit->LodBias + tObj->Sampler.LodBias != 0.0F)
- || (tObj->Sampler.MinLod != -1000.0 || tObj->Sampler.MaxLod != 1000.0);
-
- GLuint i;
-
- /* on first access create the lookup table containing the filter weights. */
- if (!weightLut) {
- create_filter_table();
- }
-
- texW = tImg->WidthScale;
- texH = tImg->HeightScale;
-
- for (i = 0; i < n; i++) {
- const GLfloat invQ = (q == 0.0F) ? 1.0F : (1.0F / q);
-
- GLfloat dudx = texW * ((s + dsdx) / (q + dqdx) - s * invQ);
- GLfloat dvdx = texH * ((t + dtdx) / (q + dqdx) - t * invQ);
- GLfloat dudy = texW * ((s + dsdy) / (q + dqdy) - s * invQ);
- GLfloat dvdy = texH * ((t + dtdy) / (q + dqdy) - t * invQ);
-
- /* note: instead of working with Px and Py, we will use the
- * squared length instead, to avoid sqrt.
- */
- GLfloat Px2 = dudx * dudx + dvdx * dvdx;
- GLfloat Py2 = dudy * dudy + dvdy * dvdy;
-
- GLfloat Pmax2;
- GLfloat Pmin2;
- GLfloat e;
- GLfloat lod;
-
- s += dsdx;
- t += dtdx;
- q += dqdx;
-
- if (Px2 < Py2) {
- Pmax2 = Py2;
- Pmin2 = Px2;
- }
- else {
- Pmax2 = Px2;
- Pmin2 = Py2;
- }
-
- /* if the eccentricity of the ellipse is too big, scale up the shorter
- * of the two vectors to limit the maximum amount of work per pixel
- */
- e = Pmax2 / Pmin2;
- if (e > maxEccentricity) {
- /* GLfloat s=e / maxEccentricity;
- minor[0] *= s;
- minor[1] *= s;
- Pmin2 *= s; */
- Pmin2 = Pmax2 / maxEccentricity;
- }
-
- /* note: we need to have Pmin=sqrt(Pmin2) here, but we can avoid
- * this since 0.5*log(x) = log(sqrt(x))
- */
- lod = 0.5 * LOG2(Pmin2);
-
- if (adjustLOD) {
- /* from swrast/s_texcombine.c _swrast_texture_span */
- if (texUnit->LodBias + tObj->Sampler.LodBias != 0.0F) {
- /* apply LOD bias, but don't clamp yet */
- const GLfloat bias =
- CLAMP(texUnit->LodBias + tObj->Sampler.LodBias,
- -ctx->Const.MaxTextureLodBias,
- ctx->Const.MaxTextureLodBias);
- lod += bias;
-
- if (tObj->Sampler.MinLod != -1000.0 ||
- tObj->Sampler.MaxLod != 1000.0) {
- /* apply LOD clamping to lambda */
- lod = CLAMP(lod, tObj->Sampler.MinLod, tObj->Sampler.MaxLod);
- }
- }
- }
-
- /* If the ellipse covers the whole image, we can
- * simply return the average of the whole image.
- */
- if (lod >= tObj->_MaxLevel) {
- sample_2d_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoords[i], rgba[i]);
- }
- else {
- /* don't bother interpolating between multiple LODs; it doesn't
- * seem to be worth the extra running time.
- */
- sample_2d_ewa(ctx, tObj, texcoords[i],
- dudx, dvdx, dudy, dvdy, floor(lod), rgba[i]);
-
- /* unused: */
- (void) sample_2d_footprint;
- /*
- sample_2d_footprint(ctx, tObj, texcoords[i],
- dudx, dvdx, dudy, dvdy, floor(lod), rgba[i]);
- */
- }
- }
-}
-
-
-
-/**********************************************************************/
-/* 3-D Texture Sampling Functions */
-/**********************************************************************/
-
-/**
- * Return the texture sample for coordinate (s,t,r) using GL_NEAREST filter.
- */
-static INLINE void
-sample_3d_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4],
- GLfloat rgba[4])
-{
- const GLint width = img->Width2; /* without border, power of two */
- const GLint height = img->Height2; /* without border, power of two */
- const GLint depth = img->Depth2; /* without border, power of two */
- GLint i, j, k;
- (void) ctx;
-
- i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]);
- j = nearest_texel_location(tObj->Sampler.WrapT, img, height, texcoord[1]);
- k = nearest_texel_location(tObj->Sampler.WrapR, img, depth, texcoord[2]);
-
- if (i < 0 || i >= (GLint) img->Width ||
- j < 0 || j >= (GLint) img->Height ||
- k < 0 || k >= (GLint) img->Depth) {
- /* Need this test for GL_CLAMP_TO_BORDER mode */
- get_border_color(tObj, img, rgba);
- }
- else {
- img->FetchTexelf(img, i, j, k, rgba);
- }
-}
-
-
-/**
- * Return the texture sample for coordinate (s,t,r) using GL_LINEAR filter.
- */
-static void
-sample_3d_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4],
- GLfloat rgba[4])
-{
- const GLint width = img->Width2;
- const GLint height = img->Height2;
- const GLint depth = img->Depth2;
- GLint i0, j0, k0, i1, j1, k1;
- GLbitfield useBorderColor = 0x0;
- GLfloat a, b, c;
- GLfloat t000[4], t010[4], t001[4], t011[4];
- GLfloat t100[4], t110[4], t101[4], t111[4];
-
- linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a);
- linear_texel_locations(tObj->Sampler.WrapT, img, height, texcoord[1], &j0, &j1, &b);
- linear_texel_locations(tObj->Sampler.WrapR, img, depth, texcoord[2], &k0, &k1, &c);
-
- if (img->Border) {
- i0 += img->Border;
- i1 += img->Border;
- j0 += img->Border;
- j1 += img->Border;
- k0 += img->Border;
- k1 += img->Border;
- }
- else {
- /* check if sampling texture border color */
- if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT;
- if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT;
- if (j0 < 0 || j0 >= height) useBorderColor |= J0BIT;
- if (j1 < 0 || j1 >= height) useBorderColor |= J1BIT;
- if (k0 < 0 || k0 >= depth) useBorderColor |= K0BIT;
- if (k1 < 0 || k1 >= depth) useBorderColor |= K1BIT;
- }
-
- /* Fetch texels */
- if (useBorderColor & (I0BIT | J0BIT | K0BIT)) {
- get_border_color(tObj, img, t000);
- }
- else {
- img->FetchTexelf(img, i0, j0, k0, t000);
- }
- if (useBorderColor & (I1BIT | J0BIT | K0BIT)) {
- get_border_color(tObj, img, t100);
- }
- else {
- img->FetchTexelf(img, i1, j0, k0, t100);
- }
- if (useBorderColor & (I0BIT | J1BIT | K0BIT)) {
- get_border_color(tObj, img, t010);
- }
- else {
- img->FetchTexelf(img, i0, j1, k0, t010);
- }
- if (useBorderColor & (I1BIT | J1BIT | K0BIT)) {
- get_border_color(tObj, img, t110);
- }
- else {
- img->FetchTexelf(img, i1, j1, k0, t110);
- }
-
- if (useBorderColor & (I0BIT | J0BIT | K1BIT)) {
- get_border_color(tObj, img, t001);
- }
- else {
- img->FetchTexelf(img, i0, j0, k1, t001);
- }
- if (useBorderColor & (I1BIT | J0BIT | K1BIT)) {
- get_border_color(tObj, img, t101);
- }
- else {
- img->FetchTexelf(img, i1, j0, k1, t101);
- }
- if (useBorderColor & (I0BIT | J1BIT | K1BIT)) {
- get_border_color(tObj, img, t011);
- }
- else {
- img->FetchTexelf(img, i0, j1, k1, t011);
- }
- if (useBorderColor & (I1BIT | J1BIT | K1BIT)) {
- get_border_color(tObj, img, t111);
- }
- else {
- img->FetchTexelf(img, i1, j1, k1, t111);
- }
-
- /* trilinear interpolation of samples */
- lerp_rgba_3d(rgba, a, b, c, t000, t100, t010, t110, t001, t101, t011, t111);
-}
-
-
-static void
-sample_3d_nearest_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4] )
-{
- GLuint i;
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_3d_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]);
- }
-}
-
-
-static void
-sample_3d_linear_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_3d_linear(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]);
- }
-}
-
-
-static void
-sample_3d_nearest_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_3d_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_3d_nearest(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0);
- sample_3d_nearest(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-static void
-sample_3d_linear_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_3d_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_3d_linear(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0);
- sample_3d_linear(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-/** Sample 3D texture, nearest filtering for both min/magnification */
-static void
-sample_nearest_3d(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- for (i = 0; i < n; i++) {
- sample_3d_nearest(ctx, tObj, image, texcoords[i], rgba[i]);
- }
-}
-
-
-/** Sample 3D texture, linear filtering for both min/magnification */
-static void
-sample_linear_3d(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- for (i = 0; i < n; i++) {
- sample_3d_linear(ctx, tObj, image, texcoords[i], rgba[i]);
- }
-}
-
-
-/** Sample 3D texture, using lambda to choose between min/magnification */
-static void
-sample_lambda_3d(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- GLuint minStart, minEnd; /* texels with minification */
- GLuint magStart, magEnd; /* texels with magnification */
- GLuint i;
-
- ASSERT(lambda != NULL);
- compute_min_mag_ranges(tObj, n, lambda,
- &minStart, &minEnd, &magStart, &magEnd);
-
- if (minStart < minEnd) {
- /* do the minified texels */
- GLuint m = minEnd - minStart;
- switch (tObj->Sampler.MinFilter) {
- case GL_NEAREST:
- for (i = minStart; i < minEnd; i++)
- sample_3d_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_LINEAR:
- for (i = minStart; i < minEnd; i++)
- sample_3d_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_NEAREST_MIPMAP_NEAREST:
- sample_3d_nearest_mipmap_nearest(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_NEAREST:
- sample_3d_linear_mipmap_nearest(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_NEAREST_MIPMAP_LINEAR:
- sample_3d_nearest_mipmap_linear(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_LINEAR:
- sample_3d_linear_mipmap_linear(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- default:
- _mesa_problem(ctx, "Bad min filter in sample_3d_texture");
- return;
- }
- }
-
- if (magStart < magEnd) {
- /* do the magnified texels */
- switch (tObj->Sampler.MagFilter) {
- case GL_NEAREST:
- for (i = magStart; i < magEnd; i++)
- sample_3d_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_LINEAR:
- for (i = magStart; i < magEnd; i++)
- sample_3d_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- default:
- _mesa_problem(ctx, "Bad mag filter in sample_3d_texture");
- return;
- }
- }
-}
-
-
-/**********************************************************************/
-/* Texture Cube Map Sampling Functions */
-/**********************************************************************/
-
-/**
- * Choose one of six sides of a texture cube map given the texture
- * coord (rx,ry,rz). Return pointer to corresponding array of texture
- * images.
- */
-static const struct gl_texture_image **
-choose_cube_face(const struct gl_texture_object *texObj,
- const GLfloat texcoord[4], GLfloat newCoord[4])
-{
- /*
- major axis
- direction target sc tc ma
- ---------- ------------------------------- --- --- ---
- +rx TEXTURE_CUBE_MAP_POSITIVE_X_EXT -rz -ry rx
- -rx TEXTURE_CUBE_MAP_NEGATIVE_X_EXT +rz -ry rx
- +ry TEXTURE_CUBE_MAP_POSITIVE_Y_EXT +rx +rz ry
- -ry TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT +rx -rz ry
- +rz TEXTURE_CUBE_MAP_POSITIVE_Z_EXT +rx -ry rz
- -rz TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT -rx -ry rz
- */
- const GLfloat rx = texcoord[0];
- const GLfloat ry = texcoord[1];
- const GLfloat rz = texcoord[2];
- const GLfloat arx = FABSF(rx), ary = FABSF(ry), arz = FABSF(rz);
- GLuint face;
- GLfloat sc, tc, ma;
-
- if (arx >= ary && arx >= arz) {
- if (rx >= 0.0F) {
- face = FACE_POS_X;
- sc = -rz;
- tc = -ry;
- ma = arx;
- }
- else {
- face = FACE_NEG_X;
- sc = rz;
- tc = -ry;
- ma = arx;
- }
- }
- else if (ary >= arx && ary >= arz) {
- if (ry >= 0.0F) {
- face = FACE_POS_Y;
- sc = rx;
- tc = rz;
- ma = ary;
- }
- else {
- face = FACE_NEG_Y;
- sc = rx;
- tc = -rz;
- ma = ary;
- }
- }
- else {
- if (rz > 0.0F) {
- face = FACE_POS_Z;
- sc = rx;
- tc = -ry;
- ma = arz;
- }
- else {
- face = FACE_NEG_Z;
- sc = -rx;
- tc = -ry;
- ma = arz;
- }
- }
-
- {
- const float ima = 1.0F / ma;
- newCoord[0] = ( sc * ima + 1.0F ) * 0.5F;
- newCoord[1] = ( tc * ima + 1.0F ) * 0.5F;
- }
-
- return (const struct gl_texture_image **) texObj->Image[face];
-}
-
-
-static void
-sample_nearest_cube(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- GLuint i;
- (void) lambda;
- for (i = 0; i < n; i++) {
- const struct gl_texture_image **images;
- GLfloat newCoord[4];
- images = choose_cube_face(tObj, texcoords[i], newCoord);
- sample_2d_nearest(ctx, tObj, images[tObj->BaseLevel],
- newCoord, rgba[i]);
- }
-}
-
-
-static void
-sample_linear_cube(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- (void) lambda;
- for (i = 0; i < n; i++) {
- const struct gl_texture_image **images;
- GLfloat newCoord[4];
- images = choose_cube_face(tObj, texcoords[i], newCoord);
- sample_2d_linear(ctx, tObj, images[tObj->BaseLevel],
- newCoord, rgba[i]);
- }
-}
-
-
-static void
-sample_cube_nearest_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- const struct gl_texture_image **images;
- GLfloat newCoord[4];
- GLint level;
- images = choose_cube_face(tObj, texcoord[i], newCoord);
-
- /* XXX we actually need to recompute lambda here based on the newCoords.
- * But we would need the texcoords of adjacent fragments to compute that
- * properly, and we don't have those here.
- * For now, do an approximation: subtracting 1 from the chosen mipmap
- * level seems to work in some test cases.
- * The same adjustment is done in the next few functions.
- */
- level = nearest_mipmap_level(tObj, lambda[i]);
- level = MAX2(level - 1, 0);
-
- sample_2d_nearest(ctx, tObj, images[level], newCoord, rgba[i]);
- }
-}
-
-
-static void
-sample_cube_linear_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- const struct gl_texture_image **images;
- GLfloat newCoord[4];
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- level = MAX2(level - 1, 0); /* see comment above */
- images = choose_cube_face(tObj, texcoord[i], newCoord);
- sample_2d_linear(ctx, tObj, images[level], newCoord, rgba[i]);
- }
-}
-
-
-static void
-sample_cube_nearest_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- const struct gl_texture_image **images;
- GLfloat newCoord[4];
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- level = MAX2(level - 1, 0); /* see comment above */
- images = choose_cube_face(tObj, texcoord[i], newCoord);
- if (level >= tObj->_MaxLevel) {
- sample_2d_nearest(ctx, tObj, images[tObj->_MaxLevel],
- newCoord, rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_2d_nearest(ctx, tObj, images[level ], newCoord, t0);
- sample_2d_nearest(ctx, tObj, images[level+1], newCoord, t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-static void
-sample_cube_linear_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- const struct gl_texture_image **images;
- GLfloat newCoord[4];
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- level = MAX2(level - 1, 0); /* see comment above */
- images = choose_cube_face(tObj, texcoord[i], newCoord);
- if (level >= tObj->_MaxLevel) {
- sample_2d_linear(ctx, tObj, images[tObj->_MaxLevel],
- newCoord, rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4];
- const GLfloat f = FRAC(lambda[i]);
- sample_2d_linear(ctx, tObj, images[level ], newCoord, t0);
- sample_2d_linear(ctx, tObj, images[level+1], newCoord, t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-/** Sample cube texture, using lambda to choose between min/magnification */
-static void
-sample_lambda_cube(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- GLuint minStart, minEnd; /* texels with minification */
- GLuint magStart, magEnd; /* texels with magnification */
-
- ASSERT(lambda != NULL);
- compute_min_mag_ranges(tObj, n, lambda,
- &minStart, &minEnd, &magStart, &magEnd);
-
- if (minStart < minEnd) {
- /* do the minified texels */
- const GLuint m = minEnd - minStart;
- switch (tObj->Sampler.MinFilter) {
- case GL_NEAREST:
- sample_nearest_cube(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR:
- sample_linear_cube(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_NEAREST_MIPMAP_NEAREST:
- sample_cube_nearest_mipmap_nearest(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_NEAREST:
- sample_cube_linear_mipmap_nearest(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_NEAREST_MIPMAP_LINEAR:
- sample_cube_nearest_mipmap_linear(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_LINEAR:
- sample_cube_linear_mipmap_linear(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- default:
- _mesa_problem(ctx, "Bad min filter in sample_lambda_cube");
- }
- }
-
- if (magStart < magEnd) {
- /* do the magnified texels */
- const GLuint m = magEnd - magStart;
- switch (tObj->Sampler.MagFilter) {
- case GL_NEAREST:
- sample_nearest_cube(ctx, tObj, m, texcoords + magStart,
- lambda + magStart, rgba + magStart);
- break;
- case GL_LINEAR:
- sample_linear_cube(ctx, tObj, m, texcoords + magStart,
- lambda + magStart, rgba + magStart);
- break;
- default:
- _mesa_problem(ctx, "Bad mag filter in sample_lambda_cube");
- }
- }
-}
-
-
-/**********************************************************************/
-/* Texture Rectangle Sampling Functions */
-/**********************************************************************/
-
-
-static void
-sample_nearest_rect(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- const struct gl_texture_image *img = tObj->Image[0][0];
- const GLint width = img->Width;
- const GLint height = img->Height;
- GLuint i;
-
- (void) ctx;
- (void) lambda;
-
- ASSERT(tObj->Sampler.WrapS == GL_CLAMP ||
- tObj->Sampler.WrapS == GL_CLAMP_TO_EDGE ||
- tObj->Sampler.WrapS == GL_CLAMP_TO_BORDER);
- ASSERT(tObj->Sampler.WrapT == GL_CLAMP ||
- tObj->Sampler.WrapT == GL_CLAMP_TO_EDGE ||
- tObj->Sampler.WrapT == GL_CLAMP_TO_BORDER);
- ASSERT(img->_BaseFormat != GL_COLOR_INDEX);
-
- for (i = 0; i < n; i++) {
- GLint row, col;
- col = clamp_rect_coord_nearest(tObj->Sampler.WrapS, texcoords[i][0], width);
- row = clamp_rect_coord_nearest(tObj->Sampler.WrapT, texcoords[i][1], height);
- if (col < 0 || col >= width || row < 0 || row >= height)
- get_border_color(tObj, img, rgba[i]);
- else
- img->FetchTexelf(img, col, row, 0, rgba[i]);
- }
-}
-
-
-static void
-sample_linear_rect(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- const struct gl_texture_image *img = tObj->Image[0][0];
- const GLint width = img->Width;
- const GLint height = img->Height;
- GLuint i;
-
- (void) ctx;
- (void) lambda;
-
- ASSERT(tObj->Sampler.WrapS == GL_CLAMP ||
- tObj->Sampler.WrapS == GL_CLAMP_TO_EDGE ||
- tObj->Sampler.WrapS == GL_CLAMP_TO_BORDER);
- ASSERT(tObj->Sampler.WrapT == GL_CLAMP ||
- tObj->Sampler.WrapT == GL_CLAMP_TO_EDGE ||
- tObj->Sampler.WrapT == GL_CLAMP_TO_BORDER);
- ASSERT(img->_BaseFormat != GL_COLOR_INDEX);
-
- for (i = 0; i < n; i++) {
- GLint i0, j0, i1, j1;
- GLfloat t00[4], t01[4], t10[4], t11[4];
- GLfloat a, b;
- GLbitfield useBorderColor = 0x0;
-
- clamp_rect_coord_linear(tObj->Sampler.WrapS, texcoords[i][0], width,
- &i0, &i1, &a);
- clamp_rect_coord_linear(tObj->Sampler.WrapT, texcoords[i][1], height,
- &j0, &j1, &b);
-
- /* compute integer rows/columns */
- if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT;
- if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT;
- if (j0 < 0 || j0 >= height) useBorderColor |= J0BIT;
- if (j1 < 0 || j1 >= height) useBorderColor |= J1BIT;
-
- /* get four texel samples */
- if (useBorderColor & (I0BIT | J0BIT))
- get_border_color(tObj, img, t00);
- else
- img->FetchTexelf(img, i0, j0, 0, t00);
-
- if (useBorderColor & (I1BIT | J0BIT))
- get_border_color(tObj, img, t10);
- else
- img->FetchTexelf(img, i1, j0, 0, t10);
-
- if (useBorderColor & (I0BIT | J1BIT))
- get_border_color(tObj, img, t01);
- else
- img->FetchTexelf(img, i0, j1, 0, t01);
-
- if (useBorderColor & (I1BIT | J1BIT))
- get_border_color(tObj, img, t11);
- else
- img->FetchTexelf(img, i1, j1, 0, t11);
-
- lerp_rgba_2d(rgba[i], a, b, t00, t10, t01, t11);
- }
-}
-
-
-/** Sample Rect texture, using lambda to choose between min/magnification */
-static void
-sample_lambda_rect(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat 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(tObj, n, lambda,
- &minStart, &minEnd, &magStart, &magEnd);
-
- if (minStart < minEnd) {
- if (tObj->Sampler.MinFilter == GL_NEAREST) {
- sample_nearest_rect(ctx, tObj, minEnd - minStart,
- texcoords + minStart, NULL, rgba + minStart);
- }
- else {
- sample_linear_rect(ctx, tObj, minEnd - minStart,
- texcoords + minStart, NULL, rgba + minStart);
- }
- }
- if (magStart < magEnd) {
- if (tObj->Sampler.MagFilter == GL_NEAREST) {
- sample_nearest_rect(ctx, tObj, magEnd - magStart,
- texcoords + magStart, NULL, rgba + magStart);
- }
- else {
- sample_linear_rect(ctx, tObj, magEnd - magStart,
- texcoords + magStart, NULL, rgba + magStart);
- }
- }
-}
-
-
-/**********************************************************************/
-/* 2D Texture Array Sampling Functions */
-/**********************************************************************/
-
-/**
- * Return the texture sample for coordinate (s,t,r) using GL_NEAREST filter.
- */
-static void
-sample_2d_array_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4],
- GLfloat rgba[4])
-{
- const GLint width = img->Width2; /* without border, power of two */
- const GLint height = img->Height2; /* without border, power of two */
- const GLint depth = img->Depth;
- GLint i, j;
- GLint array;
- (void) ctx;
-
- i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]);
- j = nearest_texel_location(tObj->Sampler.WrapT, img, height, texcoord[1]);
- array = tex_array_slice(texcoord[2], depth);
-
- if (i < 0 || i >= (GLint) img->Width ||
- j < 0 || j >= (GLint) img->Height ||
- array < 0 || array >= (GLint) img->Depth) {
- /* Need this test for GL_CLAMP_TO_BORDER mode */
- get_border_color(tObj, img, rgba);
- }
- else {
- img->FetchTexelf(img, i, j, array, rgba);
- }
-}
-
-
-/**
- * Return the texture sample for coordinate (s,t,r) using GL_LINEAR filter.
- */
-static void
-sample_2d_array_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4],
- GLfloat rgba[4])
-{
- const GLint width = img->Width2;
- const GLint height = img->Height2;
- const GLint depth = img->Depth;
- GLint i0, j0, i1, j1;
- GLint array;
- GLbitfield useBorderColor = 0x0;
- GLfloat a, b;
- GLfloat t00[4], t01[4], t10[4], t11[4];
-
- linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a);
- linear_texel_locations(tObj->Sampler.WrapT, img, height, texcoord[1], &j0, &j1, &b);
- array = tex_array_slice(texcoord[2], depth);
-
- if (array < 0 || array >= depth) {
- COPY_4V(rgba, tObj->Sampler.BorderColor.f);
- }
- else {
- if (img->Border) {
- i0 += img->Border;
- i1 += img->Border;
- j0 += img->Border;
- j1 += img->Border;
- }
- else {
- /* check if sampling texture border color */
- if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT;
- if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT;
- if (j0 < 0 || j0 >= height) useBorderColor |= J0BIT;
- if (j1 < 0 || j1 >= height) useBorderColor |= J1BIT;
- }
-
- /* Fetch texels */
- if (useBorderColor & (I0BIT | J0BIT)) {
- get_border_color(tObj, img, t00);
- }
- else {
- img->FetchTexelf(img, i0, j0, array, t00);
- }
- if (useBorderColor & (I1BIT | J0BIT)) {
- get_border_color(tObj, img, t10);
- }
- else {
- img->FetchTexelf(img, i1, j0, array, t10);
- }
- if (useBorderColor & (I0BIT | J1BIT)) {
- get_border_color(tObj, img, t01);
- }
- else {
- img->FetchTexelf(img, i0, j1, array, t01);
- }
- if (useBorderColor & (I1BIT | J1BIT)) {
- get_border_color(tObj, img, t11);
- }
- else {
- img->FetchTexelf(img, i1, j1, array, t11);
- }
-
- /* trilinear interpolation of samples */
- lerp_rgba_2d(rgba, a, b, t00, t10, t01, t11);
- }
-}
-
-
-static void
-sample_2d_array_nearest_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_2d_array_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i],
- rgba[i]);
- }
-}
-
-
-static void
-sample_2d_array_linear_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_2d_array_linear(ctx, tObj, tObj->Image[0][level],
- texcoord[i], rgba[i]);
- }
-}
-
-
-static void
-sample_2d_array_nearest_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_2d_array_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_2d_array_nearest(ctx, tObj, tObj->Image[0][level ],
- texcoord[i], t0);
- sample_2d_array_nearest(ctx, tObj, tObj->Image[0][level+1],
- texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-static void
-sample_2d_array_linear_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_2d_array_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_2d_array_linear(ctx, tObj, tObj->Image[0][level ],
- texcoord[i], t0);
- sample_2d_array_linear(ctx, tObj, tObj->Image[0][level+1],
- texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-/** Sample 2D Array texture, nearest filtering for both min/magnification */
-static void
-sample_nearest_2d_array(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- for (i = 0; i < n; i++) {
- sample_2d_array_nearest(ctx, tObj, image, texcoords[i], rgba[i]);
- }
-}
-
-
-
-/** Sample 2D Array texture, linear filtering for both min/magnification */
-static void
-sample_linear_2d_array(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- for (i = 0; i < n; i++) {
- sample_2d_array_linear(ctx, tObj, image, texcoords[i], rgba[i]);
- }
-}
-
-
-/** Sample 2D Array texture, using lambda to choose between min/magnification */
-static void
-sample_lambda_2d_array(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- GLuint minStart, minEnd; /* texels with minification */
- GLuint magStart, magEnd; /* texels with magnification */
- GLuint i;
-
- ASSERT(lambda != NULL);
- compute_min_mag_ranges(tObj, n, lambda,
- &minStart, &minEnd, &magStart, &magEnd);
-
- if (minStart < minEnd) {
- /* do the minified texels */
- GLuint m = minEnd - minStart;
- switch (tObj->Sampler.MinFilter) {
- case GL_NEAREST:
- for (i = minStart; i < minEnd; i++)
- sample_2d_array_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_LINEAR:
- for (i = minStart; i < minEnd; i++)
- sample_2d_array_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_NEAREST_MIPMAP_NEAREST:
- sample_2d_array_nearest_mipmap_nearest(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart,
- rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_NEAREST:
- sample_2d_array_linear_mipmap_nearest(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart,
- rgba + minStart);
- break;
- case GL_NEAREST_MIPMAP_LINEAR:
- sample_2d_array_nearest_mipmap_linear(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart,
- rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_LINEAR:
- sample_2d_array_linear_mipmap_linear(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart,
- rgba + minStart);
- break;
- default:
- _mesa_problem(ctx, "Bad min filter in sample_2d_array_texture");
- return;
- }
- }
-
- if (magStart < magEnd) {
- /* do the magnified texels */
- switch (tObj->Sampler.MagFilter) {
- case GL_NEAREST:
- for (i = magStart; i < magEnd; i++)
- sample_2d_array_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_LINEAR:
- for (i = magStart; i < magEnd; i++)
- sample_2d_array_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- default:
- _mesa_problem(ctx, "Bad mag filter in sample_2d_array_texture");
- return;
- }
- }
-}
-
-
-
-
-/**********************************************************************/
-/* 1D Texture Array Sampling Functions */
-/**********************************************************************/
-
-/**
- * Return the texture sample for coordinate (s,t,r) using GL_NEAREST filter.
- */
-static void
-sample_1d_array_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4],
- GLfloat rgba[4])
-{
- const GLint width = img->Width2; /* without border, power of two */
- const GLint height = img->Height;
- GLint i;
- GLint array;
- (void) ctx;
-
- i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]);
- array = tex_array_slice(texcoord[1], height);
-
- if (i < 0 || i >= (GLint) img->Width ||
- array < 0 || array >= (GLint) img->Height) {
- /* Need this test for GL_CLAMP_TO_BORDER mode */
- get_border_color(tObj, img, rgba);
- }
- else {
- img->FetchTexelf(img, i, array, 0, rgba);
- }
-}
-
-
-/**
- * Return the texture sample for coordinate (s,t,r) using GL_LINEAR filter.
- */
-static void
-sample_1d_array_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- const struct gl_texture_image *img,
- const GLfloat texcoord[4],
- GLfloat rgba[4])
-{
- const GLint width = img->Width2;
- const GLint height = img->Height;
- GLint i0, i1;
- GLint array;
- GLbitfield useBorderColor = 0x0;
- GLfloat a;
- GLfloat t0[4], t1[4];
-
- linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a);
- array = tex_array_slice(texcoord[1], height);
-
- if (img->Border) {
- i0 += img->Border;
- i1 += img->Border;
- }
- else {
- /* check if sampling texture border color */
- if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT;
- if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT;
- }
-
- if (array < 0 || array >= height) useBorderColor |= K0BIT;
-
- /* Fetch texels */
- if (useBorderColor & (I0BIT | K0BIT)) {
- get_border_color(tObj, img, t0);
- }
- else {
- img->FetchTexelf(img, i0, array, 0, t0);
- }
- if (useBorderColor & (I1BIT | K0BIT)) {
- get_border_color(tObj, img, t1);
- }
- else {
- img->FetchTexelf(img, i1, array, 0, t1);
- }
-
- /* bilinear interpolation of samples */
- lerp_rgba(rgba, a, t0, t1);
-}
-
-
-static void
-sample_1d_array_nearest_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_1d_array_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i],
- rgba[i]);
- }
-}
-
-
-static void
-sample_1d_array_linear_mipmap_nearest(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = nearest_mipmap_level(tObj, lambda[i]);
- sample_1d_array_linear(ctx, tObj, tObj->Image[0][level],
- texcoord[i], rgba[i]);
- }
-}
-
-
-static void
-sample_1d_array_nearest_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_1d_array_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_1d_array_nearest(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0);
- sample_1d_array_nearest(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-static void
-sample_1d_array_linear_mipmap_linear(struct gl_context *ctx,
- const struct gl_texture_object *tObj,
- GLuint n, const GLfloat texcoord[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- ASSERT(lambda != NULL);
- for (i = 0; i < n; i++) {
- GLint level = linear_mipmap_level(tObj, lambda[i]);
- if (level >= tObj->_MaxLevel) {
- sample_1d_array_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
- texcoord[i], rgba[i]);
- }
- else {
- GLfloat t0[4], t1[4]; /* texels */
- const GLfloat f = FRAC(lambda[i]);
- sample_1d_array_linear(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0);
- sample_1d_array_linear(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1);
- lerp_rgba(rgba[i], f, t0, t1);
- }
- }
-}
-
-
-/** Sample 1D Array texture, nearest filtering for both min/magnification */
-static void
-sample_nearest_1d_array(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- for (i = 0; i < n; i++) {
- sample_1d_array_nearest(ctx, tObj, image, texcoords[i], rgba[i]);
- }
-}
-
-
-/** Sample 1D Array texture, linear filtering for both min/magnification */
-static void
-sample_linear_1d_array(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4],
- const GLfloat lambda[], GLfloat rgba[][4])
-{
- GLuint i;
- struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
- (void) lambda;
- for (i = 0; i < n; i++) {
- sample_1d_array_linear(ctx, tObj, image, texcoords[i], rgba[i]);
- }
-}
-
-
-/** Sample 1D Array texture, using lambda to choose between min/magnification */
-static void
-sample_lambda_1d_array(struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- GLuint minStart, minEnd; /* texels with minification */
- GLuint magStart, magEnd; /* texels with magnification */
- GLuint i;
-
- ASSERT(lambda != NULL);
- compute_min_mag_ranges(tObj, n, lambda,
- &minStart, &minEnd, &magStart, &magEnd);
-
- if (minStart < minEnd) {
- /* do the minified texels */
- GLuint m = minEnd - minStart;
- switch (tObj->Sampler.MinFilter) {
- case GL_NEAREST:
- for (i = minStart; i < minEnd; i++)
- sample_1d_array_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_LINEAR:
- for (i = minStart; i < minEnd; i++)
- sample_1d_array_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_NEAREST_MIPMAP_NEAREST:
- sample_1d_array_nearest_mipmap_nearest(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_NEAREST:
- sample_1d_array_linear_mipmap_nearest(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart,
- rgba + minStart);
- break;
- case GL_NEAREST_MIPMAP_LINEAR:
- sample_1d_array_nearest_mipmap_linear(ctx, tObj, m, texcoords + minStart,
- lambda + minStart, rgba + minStart);
- break;
- case GL_LINEAR_MIPMAP_LINEAR:
- sample_1d_array_linear_mipmap_linear(ctx, tObj, m,
- texcoords + minStart,
- lambda + minStart,
- rgba + minStart);
- break;
- default:
- _mesa_problem(ctx, "Bad min filter in sample_1d_array_texture");
- return;
- }
- }
-
- if (magStart < magEnd) {
- /* do the magnified texels */
- switch (tObj->Sampler.MagFilter) {
- case GL_NEAREST:
- for (i = magStart; i < magEnd; i++)
- sample_1d_array_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- case GL_LINEAR:
- for (i = magStart; i < magEnd; i++)
- sample_1d_array_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel],
- texcoords[i], rgba[i]);
- break;
- default:
- _mesa_problem(ctx, "Bad mag filter in sample_1d_array_texture");
- return;
- }
- }
-}
-
-
-/**
- * Compare texcoord against depth sample. Return 1.0 or the ambient value.
- */
-static INLINE GLfloat
-shadow_compare(GLenum function, GLfloat coord, GLfloat depthSample,
- GLfloat ambient)
-{
- switch (function) {
- case GL_LEQUAL:
- return (coord <= depthSample) ? 1.0F : ambient;
- case GL_GEQUAL:
- return (coord >= depthSample) ? 1.0F : ambient;
- case GL_LESS:
- return (coord < depthSample) ? 1.0F : ambient;
- case GL_GREATER:
- return (coord > depthSample) ? 1.0F : ambient;
- case GL_EQUAL:
- return (coord == depthSample) ? 1.0F : ambient;
- case GL_NOTEQUAL:
- return (coord != depthSample) ? 1.0F : ambient;
- case GL_ALWAYS:
- return 1.0F;
- case GL_NEVER:
- return ambient;
- case GL_NONE:
- return depthSample;
- default:
- _mesa_problem(NULL, "Bad compare func in shadow_compare");
- return ambient;
- }
-}
-
-
-/**
- * Compare texcoord against four depth samples.
- */
-static INLINE GLfloat
-shadow_compare4(GLenum function, GLfloat coord,
- GLfloat depth00, GLfloat depth01,
- GLfloat depth10, GLfloat depth11,
- GLfloat ambient, GLfloat wi, GLfloat wj)
-{
- const GLfloat d = (1.0F - (GLfloat) ambient) * 0.25F;
- GLfloat luminance = 1.0F;
-
- switch (function) {
- case GL_LEQUAL:
- if (coord > depth00) luminance -= d;
- if (coord > depth01) luminance -= d;
- if (coord > depth10) luminance -= d;
- if (coord > depth11) luminance -= d;
- return luminance;
- case GL_GEQUAL:
- if (coord < depth00) luminance -= d;
- if (coord < depth01) luminance -= d;
- if (coord < depth10) luminance -= d;
- if (coord < depth11) luminance -= d;
- return luminance;
- case GL_LESS:
- if (coord >= depth00) luminance -= d;
- if (coord >= depth01) luminance -= d;
- if (coord >= depth10) luminance -= d;
- if (coord >= depth11) luminance -= d;
- return luminance;
- case GL_GREATER:
- if (coord <= depth00) luminance -= d;
- if (coord <= depth01) luminance -= d;
- if (coord <= depth10) luminance -= d;
- if (coord <= depth11) luminance -= d;
- return luminance;
- case GL_EQUAL:
- if (coord != depth00) luminance -= d;
- if (coord != depth01) luminance -= d;
- if (coord != depth10) luminance -= d;
- if (coord != depth11) luminance -= d;
- return luminance;
- case GL_NOTEQUAL:
- if (coord == depth00) luminance -= d;
- if (coord == depth01) luminance -= d;
- if (coord == depth10) luminance -= d;
- if (coord == depth11) luminance -= d;
- return luminance;
- case GL_ALWAYS:
- return 1.0F;
- case GL_NEVER:
- return ambient;
- case GL_NONE:
- /* ordinary bilinear filtering */
- return lerp_2d(wi, wj, depth00, depth10, depth01, depth11);
- default:
- _mesa_problem(NULL, "Bad compare func in sample_compare4");
- return ambient;
- }
-}
-
-
-/**
- * Choose the mipmap level to use when sampling from a depth texture.
- */
-static int
-choose_depth_texture_level(const struct gl_texture_object *tObj, GLfloat lambda)
-{
- GLint level;
-
- if (tObj->Sampler.MinFilter == GL_NEAREST || tObj->Sampler.MinFilter == GL_LINEAR) {
- /* no mipmapping - use base level */
- level = tObj->BaseLevel;
- }
- else {
- /* choose mipmap level */
- lambda = CLAMP(lambda, tObj->Sampler.MinLod, tObj->Sampler.MaxLod);
- level = (GLint) lambda;
- level = CLAMP(level, tObj->BaseLevel, tObj->_MaxLevel);
- }
-
- return level;
-}
-
-
-/**
- * Sample a shadow/depth texture. This function is incomplete. It doesn't
- * check for minification vs. magnification, etc.
- */
-static void
-sample_depth_texture( struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat texel[][4] )
-{
- const GLint level = choose_depth_texture_level(tObj, lambda[0]);
- const struct gl_texture_image *img = tObj->Image[0][level];
- const GLint width = img->Width;
- const GLint height = img->Height;
- const GLint depth = img->Depth;
- const GLuint compare_coord = (tObj->Target == GL_TEXTURE_2D_ARRAY_EXT)
- ? 3 : 2;
- GLfloat ambient;
- GLenum function;
- GLfloat result;
-
- ASSERT(img->_BaseFormat == GL_DEPTH_COMPONENT ||
- img->_BaseFormat == GL_DEPTH_STENCIL_EXT);
-
- ASSERT(tObj->Target == GL_TEXTURE_1D ||
- tObj->Target == GL_TEXTURE_2D ||
- tObj->Target == GL_TEXTURE_RECTANGLE_NV ||
- tObj->Target == GL_TEXTURE_1D_ARRAY_EXT ||
- tObj->Target == GL_TEXTURE_2D_ARRAY_EXT);
-
- ambient = tObj->Sampler.CompareFailValue;
-
- /* XXXX if tObj->Sampler.MinFilter != tObj->Sampler.MagFilter, we're ignoring lambda */
-
- function = (tObj->Sampler.CompareMode == GL_COMPARE_R_TO_TEXTURE_ARB) ?
- tObj->Sampler.CompareFunc : GL_NONE;
-
- if (tObj->Sampler.MagFilter == GL_NEAREST) {
- GLuint i;
- for (i = 0; i < n; i++) {
- GLfloat depthSample, depthRef;
- GLint col, row, slice;
-
- nearest_texcoord(tObj, level, texcoords[i], &col, &row, &slice);
-
- if (col >= 0 && row >= 0 && col < width && row < height &&
- slice >= 0 && slice < depth) {
- img->FetchTexelf(img, col, row, slice, &depthSample);
- }
- else {
- depthSample = tObj->Sampler.BorderColor.f[0];
- }
-
- depthRef = CLAMP(texcoords[i][compare_coord], 0.0F, 1.0F);
-
- result = shadow_compare(function, depthRef, depthSample, ambient);
-
- switch (tObj->Sampler.DepthMode) {
- case GL_LUMINANCE:
- ASSIGN_4V(texel[i], result, result, result, 1.0F);
- break;
- case GL_INTENSITY:
- ASSIGN_4V(texel[i], result, result, result, result);
- break;
- case GL_ALPHA:
- ASSIGN_4V(texel[i], 0.0F, 0.0F, 0.0F, result);
- break;
- case GL_RED:
- ASSIGN_4V(texel[i], result, 0.0F, 0.0F, 1.0F);
- break;
- default:
- _mesa_problem(ctx, "Bad depth texture mode");
- }
- }
- }
- else {
- GLuint i;
- ASSERT(tObj->Sampler.MagFilter == GL_LINEAR);
- for (i = 0; i < n; i++) {
- GLfloat depth00, depth01, depth10, depth11, depthRef;
- GLint i0, i1, j0, j1;
- GLint slice;
- GLfloat wi, wj;
- GLuint useBorderTexel;
-
- linear_texcoord(tObj, level, texcoords[i], &i0, &i1, &j0, &j1, &slice,
- &wi, &wj);
-
- useBorderTexel = 0;
- if (img->Border) {
- i0 += img->Border;
- i1 += img->Border;
- if (tObj->Target != GL_TEXTURE_1D_ARRAY_EXT) {
- j0 += img->Border;
- j1 += img->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;
- }
-
- if (slice < 0 || slice >= (GLint) depth) {
- depth00 = tObj->Sampler.BorderColor.f[0];
- depth01 = tObj->Sampler.BorderColor.f[0];
- depth10 = tObj->Sampler.BorderColor.f[0];
- depth11 = tObj->Sampler.BorderColor.f[0];
- }
- else {
- /* get four depth samples from the texture */
- if (useBorderTexel & (I0BIT | J0BIT)) {
- depth00 = tObj->Sampler.BorderColor.f[0];
- }
- else {
- img->FetchTexelf(img, i0, j0, slice, &depth00);
- }
- if (useBorderTexel & (I1BIT | J0BIT)) {
- depth10 = tObj->Sampler.BorderColor.f[0];
- }
- else {
- img->FetchTexelf(img, i1, j0, slice, &depth10);
- }
-
- if (tObj->Target != GL_TEXTURE_1D_ARRAY_EXT) {
- if (useBorderTexel & (I0BIT | J1BIT)) {
- depth01 = tObj->Sampler.BorderColor.f[0];
- }
- else {
- img->FetchTexelf(img, i0, j1, slice, &depth01);
- }
- if (useBorderTexel & (I1BIT | J1BIT)) {
- depth11 = tObj->Sampler.BorderColor.f[0];
- }
- else {
- img->FetchTexelf(img, i1, j1, slice, &depth11);
- }
- }
- else {
- depth01 = depth00;
- depth11 = depth10;
- }
- }
-
- depthRef = CLAMP(texcoords[i][compare_coord], 0.0F, 1.0F);
-
- result = shadow_compare4(function, depthRef,
- depth00, depth01, depth10, depth11,
- ambient, wi, wj);
-
- switch (tObj->Sampler.DepthMode) {
- case GL_LUMINANCE:
- ASSIGN_4V(texel[i], result, result, result, 1.0F);
- break;
- case GL_INTENSITY:
- ASSIGN_4V(texel[i], result, result, result, result);
- break;
- case GL_ALPHA:
- ASSIGN_4V(texel[i], 0.0F, 0.0F, 0.0F, result);
- break;
- default:
- _mesa_problem(ctx, "Bad depth texture mode");
- }
-
- } /* for */
- } /* if filter */
-}
-
-
-/**
- * We use this function when a texture object is in an "incomplete" state.
- * When a fragment program attempts to sample an incomplete texture we
- * return black (see issue 23 in GL_ARB_fragment_program spec).
- * Note: fragment programs don't observe the texture enable/disable flags.
- */
-static void
-null_sample_func( struct gl_context *ctx,
- const struct gl_texture_object *tObj, GLuint n,
- const GLfloat texcoords[][4], const GLfloat lambda[],
- GLfloat rgba[][4])
-{
- GLuint i;
- (void) ctx;
- (void) tObj;
- (void) texcoords;
- (void) lambda;
- for (i = 0; i < n; i++) {
- rgba[i][RCOMP] = 0;
- rgba[i][GCOMP] = 0;
- rgba[i][BCOMP] = 0;
- rgba[i][ACOMP] = 1.0;
- }
-}
-
-
-/**
- * Choose the texture sampling function for the given texture object.
- */
-texture_sample_func
-_swrast_choose_texture_sample_func( struct gl_context *ctx,
- const struct gl_texture_object *t )
-{
- if (!t || !t->_Complete) {
- return &null_sample_func;
- }
- else {
- const GLboolean needLambda =
- (GLboolean) (t->Sampler.MinFilter != t->Sampler.MagFilter);
- const GLenum format = t->Image[0][t->BaseLevel]->_BaseFormat;
-
- switch (t->Target) {
- case GL_TEXTURE_1D:
- if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL_EXT) {
- return &sample_depth_texture;
- }
- else if (needLambda) {
- return &sample_lambda_1d;
- }
- else if (t->Sampler.MinFilter == GL_LINEAR) {
- return &sample_linear_1d;
- }
- else {
- ASSERT(t->Sampler.MinFilter == GL_NEAREST);
- return &sample_nearest_1d;
- }
- case GL_TEXTURE_2D:
- if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL_EXT) {
- return &sample_depth_texture;
- }
- else if (needLambda) {
- /* Anisotropic filtering extension. Activated only if mipmaps are used */
- if (t->Sampler.MaxAnisotropy > 1.0 &&
- t->Sampler.MinFilter == GL_LINEAR_MIPMAP_LINEAR) {
- return &sample_lambda_2d_aniso;
- }
- return &sample_lambda_2d;
- }
- else if (t->Sampler.MinFilter == GL_LINEAR) {
- return &sample_linear_2d;
- }
- else {
- /* check for a few optimized cases */
- const struct gl_texture_image *img = t->Image[0][t->BaseLevel];
- ASSERT(t->Sampler.MinFilter == GL_NEAREST);
- if (t->Sampler.WrapS == GL_REPEAT &&
- t->Sampler.WrapT == GL_REPEAT &&
- img->_IsPowerOfTwo &&
- img->Border == 0 &&
- img->TexFormat == MESA_FORMAT_RGB888) {
- return &opt_sample_rgb_2d;
- }
- else if (t->Sampler.WrapS == GL_REPEAT &&
- t->Sampler.WrapT == GL_REPEAT &&
- img->_IsPowerOfTwo &&
- img->Border == 0 &&
- img->TexFormat == MESA_FORMAT_RGBA8888) {
- return &opt_sample_rgba_2d;
- }
- else {
- return &sample_nearest_2d;
- }
- }
- case GL_TEXTURE_3D:
- if (needLambda) {
- return &sample_lambda_3d;
- }
- else if (t->Sampler.MinFilter == GL_LINEAR) {
- return &sample_linear_3d;
- }
- else {
- ASSERT(t->Sampler.MinFilter == GL_NEAREST);
- return &sample_nearest_3d;
- }
- case GL_TEXTURE_CUBE_MAP:
- if (needLambda) {
- return &sample_lambda_cube;
- }
- else if (t->Sampler.MinFilter == GL_LINEAR) {
- return &sample_linear_cube;
- }
- else {
- ASSERT(t->Sampler.MinFilter == GL_NEAREST);
- return &sample_nearest_cube;
- }
- case GL_TEXTURE_RECTANGLE_NV:
- if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL_EXT) {
- return &sample_depth_texture;
- }
- else if (needLambda) {
- return &sample_lambda_rect;
- }
- else if (t->Sampler.MinFilter == GL_LINEAR) {
- return &sample_linear_rect;
- }
- else {
- ASSERT(t->Sampler.MinFilter == GL_NEAREST);
- return &sample_nearest_rect;
- }
- case GL_TEXTURE_1D_ARRAY_EXT:
- if (needLambda) {
- return &sample_lambda_1d_array;
- }
- else if (t->Sampler.MinFilter == GL_LINEAR) {
- return &sample_linear_1d_array;
- }
- else {
- ASSERT(t->Sampler.MinFilter == GL_NEAREST);
- return &sample_nearest_1d_array;
- }
- case GL_TEXTURE_2D_ARRAY_EXT:
- if (needLambda) {
- return &sample_lambda_2d_array;
- }
- else if (t->Sampler.MinFilter == GL_LINEAR) {
- return &sample_linear_2d_array;
- }
- else {
- ASSERT(t->Sampler.MinFilter == GL_NEAREST);
- return &sample_nearest_2d_array;
- }
- default:
- _mesa_problem(ctx,
- "invalid target in _swrast_choose_texture_sample_func");
- return &null_sample_func;
- }
- }
-}
+/* + * Mesa 3-D graphics library + * Version: 7.3 + * + * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "main/glheader.h" +#include "main/context.h" +#include "main/colormac.h" +#include "main/imports.h" + +#include "s_context.h" +#include "s_texfilter.h" + + +/* + * Note, the FRAC macro has to work perfectly. Otherwise you'll sometimes + * see 1-pixel bands of improperly weighted linear-filtered textures. + * The tests/texwrap.c demo is a good test. + * Also note, FRAC(x) doesn't truly return the fractional part of x for x < 0. + * Instead, if x < 0 then FRAC(x) = 1 - true_frac(x). + */ +#define FRAC(f) ((f) - IFLOOR(f)) + + + +/** + * Linear interpolation macro + */ +#define LERP(T, A, B) ( (A) + (T) * ((B) - (A)) ) + + +/** + * Do 2D/biliner interpolation of float values. + * v00, v10, v01 and v11 are typically four texture samples in a square/box. + * a and b are the horizontal and vertical interpolants. + * It's important that this function is inlined when compiled with + * optimization! If we find that's not true on some systems, convert + * to a macro. + */ +static INLINE GLfloat +lerp_2d(GLfloat a, GLfloat b, + GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11) +{ + const GLfloat temp0 = LERP(a, v00, v10); + const GLfloat temp1 = LERP(a, v01, v11); + return LERP(b, temp0, temp1); +} + + +/** + * Do 3D/trilinear interpolation of float values. + * \sa lerp_2d + */ +static INLINE GLfloat +lerp_3d(GLfloat a, GLfloat b, GLfloat c, + GLfloat v000, GLfloat v100, GLfloat v010, GLfloat v110, + GLfloat v001, GLfloat v101, GLfloat v011, GLfloat v111) +{ + const GLfloat temp00 = LERP(a, v000, v100); + const GLfloat temp10 = LERP(a, v010, v110); + const GLfloat temp01 = LERP(a, v001, v101); + const GLfloat temp11 = LERP(a, v011, v111); + const GLfloat temp0 = LERP(b, temp00, temp10); + const GLfloat temp1 = LERP(b, temp01, temp11); + return LERP(c, temp0, temp1); +} + + +/** + * Do linear interpolation of colors. + */ +static INLINE void +lerp_rgba(GLfloat result[4], GLfloat t, const GLfloat a[4], const GLfloat b[4]) +{ + result[0] = LERP(t, a[0], b[0]); + result[1] = LERP(t, a[1], b[1]); + result[2] = LERP(t, a[2], b[2]); + result[3] = LERP(t, a[3], b[3]); +} + + +/** + * Do bilinear interpolation of colors. + */ +static INLINE void +lerp_rgba_2d(GLfloat result[4], GLfloat a, GLfloat b, + const GLfloat t00[4], const GLfloat t10[4], + const GLfloat t01[4], const GLfloat t11[4]) +{ + result[0] = lerp_2d(a, b, t00[0], t10[0], t01[0], t11[0]); + result[1] = lerp_2d(a, b, t00[1], t10[1], t01[1], t11[1]); + result[2] = lerp_2d(a, b, t00[2], t10[2], t01[2], t11[2]); + result[3] = lerp_2d(a, b, t00[3], t10[3], t01[3], t11[3]); +} + + +/** + * Do trilinear interpolation of colors. + */ +static INLINE void +lerp_rgba_3d(GLfloat result[4], GLfloat a, GLfloat b, GLfloat c, + const GLfloat t000[4], const GLfloat t100[4], + const GLfloat t010[4], const GLfloat t110[4], + const GLfloat t001[4], const GLfloat t101[4], + const GLfloat t011[4], const GLfloat t111[4]) +{ + GLuint k; + /* compiler should unroll these short loops */ + for (k = 0; k < 4; k++) { + result[k] = lerp_3d(a, b, c, t000[k], t100[k], t010[k], t110[k], + t001[k], t101[k], t011[k], t111[k]); + } +} + + +/** + * Used for GL_REPEAT wrap mode. Using A % B doesn't produce the + * right results for A<0. Casting to A to be unsigned only works if B + * is a power of two. Adding a bias to A (which is a multiple of B) + * avoids the problems with A < 0 (for reasonable A) without using a + * conditional. + */ +#define REMAINDER(A, B) (((A) + (B) * 1024) % (B)) + + +/** + * Used to compute texel locations for linear sampling. + * Input: + * wrapMode = GL_REPEAT, GL_CLAMP, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER + * s = texcoord in [0,1] + * size = width (or height or depth) of texture + * Output: + * i0, i1 = returns two nearest texel indexes + * weight = returns blend factor between texels + */ +static INLINE void +linear_texel_locations(GLenum wrapMode, + const struct gl_texture_image *img, + GLint size, GLfloat s, + GLint *i0, GLint *i1, GLfloat *weight) +{ + GLfloat u; + switch (wrapMode) { + case GL_REPEAT: + u = s * size - 0.5F; + if (img->_IsPowerOfTwo) { + *i0 = IFLOOR(u) & (size - 1); + *i1 = (*i0 + 1) & (size - 1); + } + else { + *i0 = REMAINDER(IFLOOR(u), size); + *i1 = REMAINDER(*i0 + 1, size); + } + break; + case GL_CLAMP_TO_EDGE: + if (s <= 0.0F) + u = 0.0F; + else if (s >= 1.0F) + u = (GLfloat) size; + else + u = s * size; + u -= 0.5F; + *i0 = IFLOOR(u); + *i1 = *i0 + 1; + if (*i0 < 0) + *i0 = 0; + if (*i1 >= (GLint) size) + *i1 = size - 1; + break; + case GL_CLAMP_TO_BORDER: + { + const GLfloat min = -1.0F / (2.0F * size); + const GLfloat max = 1.0F - min; + if (s <= min) + u = min * size; + else if (s >= max) + u = max * size; + else + u = s * size; + u -= 0.5F; + *i0 = IFLOOR(u); + *i1 = *i0 + 1; + } + break; + case GL_MIRRORED_REPEAT: + { + const GLint flr = IFLOOR(s); + if (flr & 1) + u = 1.0F - (s - (GLfloat) flr); + else + u = s - (GLfloat) flr; + u = (u * size) - 0.5F; + *i0 = IFLOOR(u); + *i1 = *i0 + 1; + if (*i0 < 0) + *i0 = 0; + if (*i1 >= (GLint) size) + *i1 = size - 1; + } + break; + case GL_MIRROR_CLAMP_EXT: + u = FABSF(s); + if (u >= 1.0F) + u = (GLfloat) size; + else + u *= size; + u -= 0.5F; + *i0 = IFLOOR(u); + *i1 = *i0 + 1; + break; + case GL_MIRROR_CLAMP_TO_EDGE_EXT: + u = FABSF(s); + if (u >= 1.0F) + u = (GLfloat) size; + else + u *= size; + u -= 0.5F; + *i0 = IFLOOR(u); + *i1 = *i0 + 1; + if (*i0 < 0) + *i0 = 0; + if (*i1 >= (GLint) size) + *i1 = size - 1; + break; + case GL_MIRROR_CLAMP_TO_BORDER_EXT: + { + const GLfloat min = -1.0F / (2.0F * size); + const GLfloat max = 1.0F - min; + u = FABSF(s); + if (u <= min) + u = min * size; + else if (u >= max) + u = max * size; + else + u *= size; + u -= 0.5F; + *i0 = IFLOOR(u); + *i1 = *i0 + 1; + } + break; + case GL_CLAMP: + if (s <= 0.0F) + u = 0.0F; + else if (s >= 1.0F) + u = (GLfloat) size; + else + u = s * size; + u -= 0.5F; + *i0 = IFLOOR(u); + *i1 = *i0 + 1; + break; + default: + _mesa_problem(NULL, "Bad wrap mode"); + u = 0.0F; + } + *weight = FRAC(u); +} + + +/** + * Used to compute texel location for nearest sampling. + */ +static INLINE GLint +nearest_texel_location(GLenum wrapMode, + const struct gl_texture_image *img, + GLint size, GLfloat s) +{ + GLint i; + + switch (wrapMode) { + case GL_REPEAT: + /* s limited to [0,1) */ + /* i limited to [0,size-1] */ + i = IFLOOR(s * size); + if (img->_IsPowerOfTwo) + i &= (size - 1); + else + i = REMAINDER(i, size); + return i; + case GL_CLAMP_TO_EDGE: + { + /* s limited to [min,max] */ + /* i limited to [0, size-1] */ + const GLfloat min = 1.0F / (2.0F * size); + const GLfloat max = 1.0F - min; + if (s < min) + i = 0; + else if (s > max) + i = size - 1; + else + i = IFLOOR(s * size); + } + return i; + case GL_CLAMP_TO_BORDER: + { + /* s limited to [min,max] */ + /* i limited to [-1, size] */ + const GLfloat min = -1.0F / (2.0F * size); + const GLfloat max = 1.0F - min; + if (s <= min) + i = -1; + else if (s >= max) + i = size; + else + i = IFLOOR(s * size); + } + return i; + case GL_MIRRORED_REPEAT: + { + const GLfloat min = 1.0F / (2.0F * size); + const GLfloat max = 1.0F - min; + const GLint flr = IFLOOR(s); + GLfloat u; + if (flr & 1) + u = 1.0F - (s - (GLfloat) flr); + else + u = s - (GLfloat) flr; + if (u < min) + i = 0; + else if (u > max) + i = size - 1; + else + i = IFLOOR(u * size); + } + return i; + case GL_MIRROR_CLAMP_EXT: + { + /* s limited to [0,1] */ + /* i limited to [0,size-1] */ + const GLfloat u = FABSF(s); + if (u <= 0.0F) + i = 0; + else if (u >= 1.0F) + i = size - 1; + else + i = IFLOOR(u * size); + } + return i; + case GL_MIRROR_CLAMP_TO_EDGE_EXT: + { + /* s limited to [min,max] */ + /* i limited to [0, size-1] */ + const GLfloat min = 1.0F / (2.0F * size); + const GLfloat max = 1.0F - min; + const GLfloat u = FABSF(s); + if (u < min) + i = 0; + else if (u > max) + i = size - 1; + else + i = IFLOOR(u * size); + } + return i; + case GL_MIRROR_CLAMP_TO_BORDER_EXT: + { + /* s limited to [min,max] */ + /* i limited to [0, size-1] */ + const GLfloat min = -1.0F / (2.0F * size); + const GLfloat max = 1.0F - min; + const GLfloat u = FABSF(s); + if (u < min) + i = -1; + else if (u > max) + i = size; + else + i = IFLOOR(u * size); + } + return i; + case GL_CLAMP: + /* s limited to [0,1] */ + /* i limited to [0,size-1] */ + if (s <= 0.0F) + i = 0; + else if (s >= 1.0F) + i = size - 1; + else + i = IFLOOR(s * size); + return i; + default: + _mesa_problem(NULL, "Bad wrap mode"); + return 0; + } +} + + +/* Power of two image sizes only */ +static INLINE void +linear_repeat_texel_location(GLuint size, GLfloat s, + GLint *i0, GLint *i1, GLfloat *weight) +{ + GLfloat u = s * size - 0.5F; + *i0 = IFLOOR(u) & (size - 1); + *i1 = (*i0 + 1) & (size - 1); + *weight = FRAC(u); +} + + +/** + * Do clamp/wrap for a texture rectangle coord, GL_NEAREST filter mode. + */ +static INLINE GLint +clamp_rect_coord_nearest(GLenum wrapMode, GLfloat coord, GLint max) +{ + switch (wrapMode) { + case GL_CLAMP: + return IFLOOR( CLAMP(coord, 0.0F, max - 1) ); + case GL_CLAMP_TO_EDGE: + return IFLOOR( CLAMP(coord, 0.5F, max - 0.5F) ); + case GL_CLAMP_TO_BORDER: + return IFLOOR( CLAMP(coord, -0.5F, max + 0.5F) ); + default: + _mesa_problem(NULL, "bad wrapMode in clamp_rect_coord_nearest"); + return 0; + } +} + + +/** + * As above, but GL_LINEAR filtering. + */ +static INLINE void +clamp_rect_coord_linear(GLenum wrapMode, GLfloat coord, GLint max, + GLint *i0out, GLint *i1out, GLfloat *weight) +{ + GLfloat fcol; + GLint i0, i1; + switch (wrapMode) { + case GL_CLAMP: + /* Not exactly what the spec says, but it matches NVIDIA output */ + fcol = CLAMP(coord - 0.5F, 0.0F, max - 1); + i0 = IFLOOR(fcol); + i1 = i0 + 1; + break; + case GL_CLAMP_TO_EDGE: + fcol = CLAMP(coord, 0.5F, max - 0.5F); + fcol -= 0.5F; + i0 = IFLOOR(fcol); + i1 = i0 + 1; + if (i1 > max - 1) + i1 = max - 1; + break; + case GL_CLAMP_TO_BORDER: + fcol = CLAMP(coord, -0.5F, max + 0.5F); + fcol -= 0.5F; + i0 = IFLOOR(fcol); + i1 = i0 + 1; + break; + default: + _mesa_problem(NULL, "bad wrapMode in clamp_rect_coord_linear"); + i0 = i1 = 0; + fcol = 0.0F; + } + *i0out = i0; + *i1out = i1; + *weight = FRAC(fcol); +} + + +/** + * Compute slice/image to use for 1D or 2D array texture. + */ +static INLINE GLint +tex_array_slice(GLfloat coord, GLsizei size) +{ + GLint slice = IFLOOR(coord + 0.5f); + slice = CLAMP(slice, 0, size - 1); + return slice; +} + + +/** + * Compute nearest integer texcoords for given texobj and coordinate. + * NOTE: only used for depth texture sampling. + */ +static INLINE void +nearest_texcoord(const struct gl_texture_object *texObj, + GLuint level, + const GLfloat texcoord[4], + GLint *i, GLint *j, GLint *k) +{ + const struct gl_texture_image *img = texObj->Image[0][level]; + const GLint width = img->Width; + const GLint height = img->Height; + const GLint depth = img->Depth; + + switch (texObj->Target) { + case GL_TEXTURE_RECTANGLE_ARB: + *i = clamp_rect_coord_nearest(texObj->Sampler.WrapS, texcoord[0], width); + *j = clamp_rect_coord_nearest(texObj->Sampler.WrapT, texcoord[1], height); + *k = 0; + break; + case GL_TEXTURE_1D: + *i = nearest_texel_location(texObj->Sampler.WrapS, img, width, texcoord[0]); + *j = 0; + *k = 0; + break; + case GL_TEXTURE_2D: + *i = nearest_texel_location(texObj->Sampler.WrapS, img, width, texcoord[0]); + *j = nearest_texel_location(texObj->Sampler.WrapT, img, height, texcoord[1]); + *k = 0; + break; + case GL_TEXTURE_1D_ARRAY_EXT: + *i = nearest_texel_location(texObj->Sampler.WrapS, img, width, texcoord[0]); + *j = tex_array_slice(texcoord[1], height); + *k = 0; + break; + case GL_TEXTURE_2D_ARRAY_EXT: + *i = nearest_texel_location(texObj->Sampler.WrapS, img, width, texcoord[0]); + *j = nearest_texel_location(texObj->Sampler.WrapT, img, height, texcoord[1]); + *k = tex_array_slice(texcoord[2], depth); + break; + default: + *i = *j = *k = 0; + } +} + + +/** + * Compute linear integer texcoords for given texobj and coordinate. + * NOTE: only used for depth texture sampling. + */ +static INLINE void +linear_texcoord(const struct gl_texture_object *texObj, + GLuint level, + const GLfloat texcoord[4], + GLint *i0, GLint *i1, GLint *j0, GLint *j1, GLint *slice, + GLfloat *wi, GLfloat *wj) +{ + const struct gl_texture_image *img = texObj->Image[0][level]; + const GLint width = img->Width; + const GLint height = img->Height; + const GLint depth = img->Depth; + + switch (texObj->Target) { + case GL_TEXTURE_RECTANGLE_ARB: + clamp_rect_coord_linear(texObj->Sampler.WrapS, texcoord[0], + width, i0, i1, wi); + clamp_rect_coord_linear(texObj->Sampler.WrapT, texcoord[1], + height, j0, j1, wj); + *slice = 0; + break; + + case GL_TEXTURE_1D: + case GL_TEXTURE_2D: + linear_texel_locations(texObj->Sampler.WrapS, img, width, + texcoord[0], i0, i1, wi); + linear_texel_locations(texObj->Sampler.WrapT, img, height, + texcoord[1], j0, j1, wj); + *slice = 0; + break; + + case GL_TEXTURE_1D_ARRAY_EXT: + linear_texel_locations(texObj->Sampler.WrapS, img, width, + texcoord[0], i0, i1, wi); + *j0 = tex_array_slice(texcoord[1], height); + *j1 = *j0; + *slice = 0; + break; + + case GL_TEXTURE_2D_ARRAY_EXT: + linear_texel_locations(texObj->Sampler.WrapS, img, width, + texcoord[0], i0, i1, wi); + linear_texel_locations(texObj->Sampler.WrapT, img, height, + texcoord[1], j0, j1, wj); + *slice = tex_array_slice(texcoord[2], depth); + break; + + default: + *slice = 0; + } +} + + + +/** + * For linear interpolation between mipmap levels N and N+1, this function + * computes N. + */ +static INLINE GLint +linear_mipmap_level(const struct gl_texture_object *tObj, GLfloat lambda) +{ + if (lambda < 0.0F) + return tObj->BaseLevel; + else if (lambda > tObj->_MaxLambda) + return (GLint) (tObj->BaseLevel + tObj->_MaxLambda); + else + return (GLint) (tObj->BaseLevel + lambda); +} + + +/** + * Compute the nearest mipmap level to take texels from. + */ +static INLINE GLint +nearest_mipmap_level(const struct gl_texture_object *tObj, GLfloat lambda) +{ + GLfloat l; + GLint level; + if (lambda <= 0.5F) + l = 0.0F; + else if (lambda > tObj->_MaxLambda + 0.4999F) + l = tObj->_MaxLambda + 0.4999F; + else + l = lambda; + level = (GLint) (tObj->BaseLevel + l + 0.5F); + if (level > tObj->_MaxLevel) + level = tObj->_MaxLevel; + return level; +} + + + +/* + * Bitflags for texture border color sampling. + */ +#define I0BIT 1 +#define I1BIT 2 +#define J0BIT 4 +#define J1BIT 8 +#define K0BIT 16 +#define K1BIT 32 + + + +/** + * The lambda[] array values are always monotonic. Either the whole span + * will be minified, magnified, or split between the two. This function + * determines the subranges in [0, n-1] that are to be minified or magnified. + */ +static INLINE void +compute_min_mag_ranges(const struct gl_texture_object *tObj, + GLuint n, const GLfloat lambda[], + GLuint *minStart, GLuint *minEnd, + GLuint *magStart, GLuint *magEnd) +{ + GLfloat minMagThresh; + + /* we shouldn't be here if minfilter == magfilter */ + ASSERT(tObj->Sampler.MinFilter != tObj->Sampler.MagFilter); + + /* This bit comes from the OpenGL spec: */ + if (tObj->Sampler.MagFilter == GL_LINEAR + && (tObj->Sampler.MinFilter == GL_NEAREST_MIPMAP_NEAREST || + tObj->Sampler.MinFilter == GL_NEAREST_MIPMAP_LINEAR)) { + minMagThresh = 0.5F; + } + else { + minMagThresh = 0.0F; + } + +#if 0 + /* DEBUG CODE: Verify that lambda[] is monotonic. + * We can't really use this because the inaccuracy in the LOG2 function + * causes this test to fail, yet the resulting texturing is correct. + */ + if (n > 1) { + GLuint i; + printf("lambda delta = %g\n", lambda[0] - lambda[n-1]); + if (lambda[0] >= lambda[n-1]) { /* decreasing */ + for (i = 0; i < n - 1; i++) { + ASSERT((GLint) (lambda[i] * 10) >= (GLint) (lambda[i+1] * 10)); + } + } + else { /* increasing */ + for (i = 0; i < n - 1; i++) { + ASSERT((GLint) (lambda[i] * 10) <= (GLint) (lambda[i+1] * 10)); + } + } + } +#endif /* DEBUG */ + + if (lambda[0] <= minMagThresh && (n <= 1 || lambda[n-1] <= minMagThresh)) { + /* magnification for whole span */ + *magStart = 0; + *magEnd = n; + *minStart = *minEnd = 0; + } + else if (lambda[0] > minMagThresh && (n <=1 || lambda[n-1] > minMagThresh)) { + /* minification for whole span */ + *minStart = 0; + *minEnd = n; + *magStart = *magEnd = 0; + } + else { + /* a mix of minification and magnification */ + GLuint i; + if (lambda[0] > minMagThresh) { + /* start with minification */ + for (i = 1; i < n; i++) { + if (lambda[i] <= minMagThresh) + break; + } + *minStart = 0; + *minEnd = i; + *magStart = i; + *magEnd = n; + } + else { + /* start with magnification */ + for (i = 1; i < n; i++) { + if (lambda[i] > minMagThresh) + break; + } + *magStart = 0; + *magEnd = i; + *minStart = i; + *minEnd = n; + } + } + +#if 0 + /* Verify the min/mag Start/End values + * We don't use this either (see above) + */ + { + GLint i; + for (i = 0; i < n; i++) { + if (lambda[i] > minMagThresh) { + /* minification */ + ASSERT(i >= *minStart); + ASSERT(i < *minEnd); + } + else { + /* magnification */ + ASSERT(i >= *magStart); + ASSERT(i < *magEnd); + } + } + } +#endif +} + + +/** + * When we sample the border color, it must be interpreted according to + * the base texture format. Ex: if the texture base format it GL_ALPHA, + * we return (0,0,0,BorderAlpha). + */ +static INLINE void +get_border_color(const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + GLfloat rgba[4]) +{ + switch (img->_BaseFormat) { + case GL_RGB: + rgba[0] = tObj->Sampler.BorderColor.f[0]; + rgba[1] = tObj->Sampler.BorderColor.f[1]; + rgba[2] = tObj->Sampler.BorderColor.f[2]; + rgba[3] = 1.0F; + break; + case GL_ALPHA: + rgba[0] = rgba[1] = rgba[2] = 0.0; + rgba[3] = tObj->Sampler.BorderColor.f[3]; + break; + case GL_LUMINANCE: + rgba[0] = rgba[1] = rgba[2] = tObj->Sampler.BorderColor.f[0]; + rgba[3] = 1.0; + break; + case GL_LUMINANCE_ALPHA: + rgba[0] = rgba[1] = rgba[2] = tObj->Sampler.BorderColor.f[0]; + rgba[3] = tObj->Sampler.BorderColor.f[3]; + break; + case GL_INTENSITY: + rgba[0] = rgba[1] = rgba[2] = rgba[3] = tObj->Sampler.BorderColor.f[0]; + break; + default: + COPY_4V(rgba, tObj->Sampler.BorderColor.f); + } +} + + +/**********************************************************************/ +/* 1-D Texture Sampling Functions */ +/**********************************************************************/ + +/** + * Return the texture sample for coordinate (s) using GL_NEAREST filter. + */ +static INLINE void +sample_1d_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], GLfloat rgba[4]) +{ + const GLint width = img->Width2; /* without border, power of two */ + GLint i; + i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]); + /* skip over the border, if any */ + i += img->Border; + if (i < 0 || i >= (GLint) img->Width) { + /* Need this test for GL_CLAMP_TO_BORDER mode */ + get_border_color(tObj, img, rgba); + } + else { + img->FetchTexelf(img, i, 0, 0, rgba); + } +} + + +/** + * Return the texture sample for coordinate (s) using GL_LINEAR filter. + */ +static INLINE void +sample_1d_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], GLfloat rgba[4]) +{ + const GLint width = img->Width2; + GLint i0, i1; + GLbitfield useBorderColor = 0x0; + GLfloat a; + GLfloat t0[4], t1[4]; /* texels */ + + linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a); + + if (img->Border) { + i0 += img->Border; + i1 += img->Border; + } + else { + if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT; + if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT; + } + + /* fetch texel colors */ + if (useBorderColor & I0BIT) { + get_border_color(tObj, img, t0); + } + else { + img->FetchTexelf(img, i0, 0, 0, t0); + } + if (useBorderColor & I1BIT) { + get_border_color(tObj, img, t1); + } + else { + img->FetchTexelf(img, i1, 0, 0, t1); + } + + lerp_rgba(rgba, a, t0, t1); +} + + +static void +sample_1d_nearest_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_1d_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]); + } +} + + +static void +sample_1d_linear_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_1d_linear(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]); + } +} + + +static void +sample_1d_nearest_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_1d_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; + const GLfloat f = FRAC(lambda[i]); + sample_1d_nearest(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0); + sample_1d_nearest(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +static void +sample_1d_linear_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_1d_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; + const GLfloat f = FRAC(lambda[i]); + sample_1d_linear(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0); + sample_1d_linear(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +/** Sample 1D texture, nearest filtering for both min/magnification */ +static void +sample_nearest_1d( struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4] ) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + for (i = 0; i < n; i++) { + sample_1d_nearest(ctx, tObj, image, texcoords[i], rgba[i]); + } +} + + +/** Sample 1D texture, linear filtering for both min/magnification */ +static void +sample_linear_1d( struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4] ) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + for (i = 0; i < n; i++) { + sample_1d_linear(ctx, tObj, image, texcoords[i], rgba[i]); + } +} + + +/** Sample 1D texture, using lambda to choose between min/magnification */ +static void +sample_lambda_1d( struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4] ) +{ + GLuint minStart, minEnd; /* texels with minification */ + GLuint magStart, magEnd; /* texels with magnification */ + GLuint i; + + ASSERT(lambda != NULL); + compute_min_mag_ranges(tObj, n, lambda, + &minStart, &minEnd, &magStart, &magEnd); + + if (minStart < minEnd) { + /* do the minified texels */ + const GLuint m = minEnd - minStart; + switch (tObj->Sampler.MinFilter) { + case GL_NEAREST: + for (i = minStart; i < minEnd; i++) + sample_1d_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_LINEAR: + for (i = minStart; i < minEnd; i++) + sample_1d_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_NEAREST_MIPMAP_NEAREST: + sample_1d_nearest_mipmap_nearest(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_NEAREST: + sample_1d_linear_mipmap_nearest(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_NEAREST_MIPMAP_LINEAR: + sample_1d_nearest_mipmap_linear(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_LINEAR: + sample_1d_linear_mipmap_linear(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + default: + _mesa_problem(ctx, "Bad min filter in sample_1d_texture"); + return; + } + } + + if (magStart < magEnd) { + /* do the magnified texels */ + switch (tObj->Sampler.MagFilter) { + case GL_NEAREST: + for (i = magStart; i < magEnd; i++) + sample_1d_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_LINEAR: + for (i = magStart; i < magEnd; i++) + sample_1d_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + default: + _mesa_problem(ctx, "Bad mag filter in sample_1d_texture"); + return; + } + } +} + + +/**********************************************************************/ +/* 2-D Texture Sampling Functions */ +/**********************************************************************/ + + +/** + * Return the texture sample for coordinate (s,t) using GL_NEAREST filter. + */ +static INLINE void +sample_2d_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], + GLfloat rgba[]) +{ + const GLint width = img->Width2; /* without border, power of two */ + const GLint height = img->Height2; /* without border, power of two */ + GLint i, j; + (void) ctx; + + i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]); + j = nearest_texel_location(tObj->Sampler.WrapT, img, height, texcoord[1]); + + /* skip over the border, if any */ + i += img->Border; + j += img->Border; + + if (i < 0 || i >= (GLint) img->Width || j < 0 || j >= (GLint) img->Height) { + /* Need this test for GL_CLAMP_TO_BORDER mode */ + get_border_color(tObj, img, rgba); + } + else { + img->FetchTexelf(img, i, j, 0, rgba); + } +} + + +/** + * Return the texture sample for coordinate (s,t) using GL_LINEAR filter. + * New sampling code contributed by Lynn Quam <quam@ai.sri.com>. + */ +static INLINE void +sample_2d_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], + GLfloat rgba[]) +{ + const GLint width = img->Width2; + const GLint height = img->Height2; + GLint i0, j0, i1, j1; + GLbitfield useBorderColor = 0x0; + GLfloat a, b; + GLfloat t00[4], t10[4], t01[4], t11[4]; /* sampled texel colors */ + + linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a); + linear_texel_locations(tObj->Sampler.WrapT, img, height, texcoord[1], &j0, &j1, &b); + + if (img->Border) { + i0 += img->Border; + i1 += img->Border; + j0 += img->Border; + j1 += img->Border; + } + else { + if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT; + if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT; + if (j0 < 0 || j0 >= height) useBorderColor |= J0BIT; + if (j1 < 0 || j1 >= height) useBorderColor |= J1BIT; + } + + /* fetch four texel colors */ + if (useBorderColor & (I0BIT | J0BIT)) { + get_border_color(tObj, img, t00); + } + else { + img->FetchTexelf(img, i0, j0, 0, t00); + } + if (useBorderColor & (I1BIT | J0BIT)) { + get_border_color(tObj, img, t10); + } + else { + img->FetchTexelf(img, i1, j0, 0, t10); + } + if (useBorderColor & (I0BIT | J1BIT)) { + get_border_color(tObj, img, t01); + } + else { + img->FetchTexelf(img, i0, j1, 0, t01); + } + if (useBorderColor & (I1BIT | J1BIT)) { + get_border_color(tObj, img, t11); + } + else { + img->FetchTexelf(img, i1, j1, 0, t11); + } + + lerp_rgba_2d(rgba, a, b, t00, t10, t01, t11); +} + + +/** + * As above, but we know WRAP_S == REPEAT and WRAP_T == REPEAT. + * We don't have to worry about the texture border. + */ +static INLINE void +sample_2d_linear_repeat(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], + GLfloat rgba[]) +{ + const GLint width = img->Width2; + const GLint height = img->Height2; + GLint i0, j0, i1, j1; + GLfloat wi, wj; + GLfloat t00[4], t10[4], t01[4], t11[4]; /* sampled texel colors */ + + (void) ctx; + + ASSERT(tObj->Sampler.WrapS == GL_REPEAT); + ASSERT(tObj->Sampler.WrapT == GL_REPEAT); + ASSERT(img->Border == 0); + ASSERT(img->_IsPowerOfTwo); + + linear_repeat_texel_location(width, texcoord[0], &i0, &i1, &wi); + linear_repeat_texel_location(height, texcoord[1], &j0, &j1, &wj); + + img->FetchTexelf(img, i0, j0, 0, t00); + img->FetchTexelf(img, i1, j0, 0, t10); + img->FetchTexelf(img, i0, j1, 0, t01); + img->FetchTexelf(img, i1, j1, 0, t11); + + lerp_rgba_2d(rgba, wi, wj, t00, t10, t01, t11); +} + + +static void +sample_2d_nearest_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_2d_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]); + } +} + + +static void +sample_2d_linear_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_2d_linear(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]); + } +} + + +static void +sample_2d_nearest_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_2d_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_2d_nearest(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0); + sample_2d_nearest(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +static void +sample_2d_linear_mipmap_linear( struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4] ) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_2d_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_2d_linear(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0); + sample_2d_linear(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +static void +sample_2d_linear_mipmap_linear_repeat(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + ASSERT(tObj->Sampler.WrapS == GL_REPEAT); + ASSERT(tObj->Sampler.WrapT == GL_REPEAT); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_2d_linear_repeat(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_2d_linear_repeat(ctx, tObj, tObj->Image[0][level ], + texcoord[i], t0); + sample_2d_linear_repeat(ctx, tObj, tObj->Image[0][level+1], + texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +/** Sample 2D texture, nearest filtering for both min/magnification */ +static void +sample_nearest_2d(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + for (i = 0; i < n; i++) { + sample_2d_nearest(ctx, tObj, image, texcoords[i], rgba[i]); + } +} + + +/** Sample 2D texture, linear filtering for both min/magnification */ +static void +sample_linear_2d(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + if (tObj->Sampler.WrapS == GL_REPEAT && + tObj->Sampler.WrapT == GL_REPEAT && + image->_IsPowerOfTwo && + image->Border == 0) { + for (i = 0; i < n; i++) { + sample_2d_linear_repeat(ctx, tObj, image, texcoords[i], rgba[i]); + } + } + else { + for (i = 0; i < n; i++) { + sample_2d_linear(ctx, tObj, image, texcoords[i], rgba[i]); + } + } +} + + +/** + * Optimized 2-D texture sampling: + * S and T wrap mode == GL_REPEAT + * GL_NEAREST min/mag filter + * No border, + * RowStride == Width, + * Format = GL_RGB + */ +static void +opt_sample_rgb_2d(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + const struct gl_texture_image *img = tObj->Image[0][tObj->BaseLevel]; + const GLfloat width = (GLfloat) img->Width; + const GLfloat height = (GLfloat) img->Height; + const GLint colMask = img->Width - 1; + const GLint rowMask = img->Height - 1; + const GLint shift = img->WidthLog2; + GLuint k; + (void) ctx; + (void) lambda; + ASSERT(tObj->Sampler.WrapS==GL_REPEAT); + ASSERT(tObj->Sampler.WrapT==GL_REPEAT); + ASSERT(img->Border==0); + ASSERT(img->TexFormat == MESA_FORMAT_RGB888); + ASSERT(img->_IsPowerOfTwo); + + for (k=0; k<n; k++) { + GLint i = IFLOOR(texcoords[k][0] * width) & colMask; + GLint j = IFLOOR(texcoords[k][1] * height) & rowMask; + GLint pos = (j << shift) | i; + GLubyte *texel = ((GLubyte *) img->Data) + 3*pos; + rgba[k][RCOMP] = UBYTE_TO_FLOAT(texel[2]); + rgba[k][GCOMP] = UBYTE_TO_FLOAT(texel[1]); + rgba[k][BCOMP] = UBYTE_TO_FLOAT(texel[0]); + rgba[k][ACOMP] = 1.0F; + } +} + + +/** + * Optimized 2-D texture sampling: + * S and T wrap mode == GL_REPEAT + * GL_NEAREST min/mag filter + * No border + * RowStride == Width, + * Format = GL_RGBA + */ +static void +opt_sample_rgba_2d(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + const struct gl_texture_image *img = tObj->Image[0][tObj->BaseLevel]; + const GLfloat width = (GLfloat) img->Width; + const GLfloat height = (GLfloat) img->Height; + const GLint colMask = img->Width - 1; + const GLint rowMask = img->Height - 1; + const GLint shift = img->WidthLog2; + GLuint i; + (void) ctx; + (void) lambda; + ASSERT(tObj->Sampler.WrapS==GL_REPEAT); + ASSERT(tObj->Sampler.WrapT==GL_REPEAT); + ASSERT(img->Border==0); + ASSERT(img->TexFormat == MESA_FORMAT_RGBA8888); + ASSERT(img->_IsPowerOfTwo); + + for (i = 0; i < n; i++) { + const GLint col = IFLOOR(texcoords[i][0] * width) & colMask; + const GLint row = IFLOOR(texcoords[i][1] * height) & rowMask; + const GLint pos = (row << shift) | col; + const GLuint texel = *((GLuint *) img->Data + pos); + rgba[i][RCOMP] = UBYTE_TO_FLOAT( (texel >> 24) ); + rgba[i][GCOMP] = UBYTE_TO_FLOAT( (texel >> 16) & 0xff ); + rgba[i][BCOMP] = UBYTE_TO_FLOAT( (texel >> 8) & 0xff ); + rgba[i][ACOMP] = UBYTE_TO_FLOAT( (texel ) & 0xff ); + } +} + + +/** Sample 2D texture, using lambda to choose between min/magnification */ +static void +sample_lambda_2d(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + const struct gl_texture_image *tImg = tObj->Image[0][tObj->BaseLevel]; + GLuint minStart, minEnd; /* texels with minification */ + GLuint magStart, magEnd; /* texels with magnification */ + + const GLboolean repeatNoBorderPOT = (tObj->Sampler.WrapS == GL_REPEAT) + && (tObj->Sampler.WrapT == GL_REPEAT) + && (tImg->Border == 0 && (tImg->Width == tImg->RowStride)) + && tImg->_IsPowerOfTwo; + + ASSERT(lambda != NULL); + compute_min_mag_ranges(tObj, n, lambda, + &minStart, &minEnd, &magStart, &magEnd); + + if (minStart < minEnd) { + /* do the minified texels */ + const GLuint m = minEnd - minStart; + switch (tObj->Sampler.MinFilter) { + case GL_NEAREST: + if (repeatNoBorderPOT) { + switch (tImg->TexFormat) { + case MESA_FORMAT_RGB888: + opt_sample_rgb_2d(ctx, tObj, m, texcoords + minStart, + NULL, rgba + minStart); + break; + case MESA_FORMAT_RGBA8888: + opt_sample_rgba_2d(ctx, tObj, m, texcoords + minStart, + NULL, rgba + minStart); + break; + default: + sample_nearest_2d(ctx, tObj, m, texcoords + minStart, + NULL, rgba + minStart ); + } + } + else { + sample_nearest_2d(ctx, tObj, m, texcoords + minStart, + NULL, rgba + minStart); + } + break; + case GL_LINEAR: + sample_linear_2d(ctx, tObj, m, texcoords + minStart, + NULL, rgba + minStart); + break; + case GL_NEAREST_MIPMAP_NEAREST: + sample_2d_nearest_mipmap_nearest(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_NEAREST: + sample_2d_linear_mipmap_nearest(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_NEAREST_MIPMAP_LINEAR: + sample_2d_nearest_mipmap_linear(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_LINEAR: + if (repeatNoBorderPOT) + sample_2d_linear_mipmap_linear_repeat(ctx, tObj, m, + texcoords + minStart, lambda + minStart, rgba + minStart); + else + sample_2d_linear_mipmap_linear(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + default: + _mesa_problem(ctx, "Bad min filter in sample_2d_texture"); + return; + } + } + + if (magStart < magEnd) { + /* do the magnified texels */ + const GLuint m = magEnd - magStart; + + switch (tObj->Sampler.MagFilter) { + case GL_NEAREST: + if (repeatNoBorderPOT) { + switch (tImg->TexFormat) { + case MESA_FORMAT_RGB888: + opt_sample_rgb_2d(ctx, tObj, m, texcoords + magStart, + NULL, rgba + magStart); + break; + case MESA_FORMAT_RGBA8888: + opt_sample_rgba_2d(ctx, tObj, m, texcoords + magStart, + NULL, rgba + magStart); + break; + default: + sample_nearest_2d(ctx, tObj, m, texcoords + magStart, + NULL, rgba + magStart ); + } + } + else { + sample_nearest_2d(ctx, tObj, m, texcoords + magStart, + NULL, rgba + magStart); + } + break; + case GL_LINEAR: + sample_linear_2d(ctx, tObj, m, texcoords + magStart, + NULL, rgba + magStart); + break; + default: + _mesa_problem(ctx, "Bad mag filter in sample_lambda_2d"); + } + } +} + + +/* For anisotropic filtering */ +#define WEIGHT_LUT_SIZE 1024 + +static GLfloat *weightLut = NULL; + +/** + * Creates the look-up table used to speed-up EWA sampling + */ +static void +create_filter_table(void) +{ + GLuint i; + if (!weightLut) { + weightLut = (GLfloat *) malloc(WEIGHT_LUT_SIZE * sizeof(GLfloat)); + + for (i = 0; i < WEIGHT_LUT_SIZE; ++i) { + GLfloat alpha = 2; + GLfloat r2 = (GLfloat) i / (GLfloat) (WEIGHT_LUT_SIZE - 1); + GLfloat weight = (GLfloat) exp(-alpha * r2); + weightLut[i] = weight; + } + } +} + + +/** + * Elliptical weighted average (EWA) filter for producing high quality + * anisotropic filtered results. + * Based on the Higher Quality Elliptical Weighted Avarage Filter + * published by Paul S. Heckbert in his Master's Thesis + * "Fundamentals of Texture Mapping and Image Warping" (1989) + */ +static void +sample_2d_ewa(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const GLfloat texcoord[4], + const GLfloat dudx, const GLfloat dvdx, + const GLfloat dudy, const GLfloat dvdy, const GLint lod, + GLfloat rgba[]) +{ + GLint level = lod > 0 ? lod : 0; + GLfloat scaling = 1.0 / (1 << level); + const struct gl_texture_image *img = tObj->Image[0][level]; + const struct gl_texture_image *mostDetailedImage = + tObj->Image[0][tObj->BaseLevel]; + GLfloat tex_u=-0.5 + texcoord[0] * mostDetailedImage->WidthScale * scaling; + GLfloat tex_v=-0.5 + texcoord[1] * mostDetailedImage->HeightScale * scaling; + + GLfloat ux = dudx * scaling; + GLfloat vx = dvdx * scaling; + GLfloat uy = dudy * scaling; + GLfloat vy = dvdy * scaling; + + /* compute ellipse coefficients to bound the region: + * A*x*x + B*x*y + C*y*y = F. + */ + GLfloat A = vx*vx+vy*vy+1; + GLfloat B = -2*(ux*vx+uy*vy); + GLfloat C = ux*ux+uy*uy+1; + GLfloat F = A*C-B*B/4.0; + + /* check if it is an ellipse */ + /* ASSERT(F > 0.0); */ + + /* Compute the ellipse's (u,v) bounding box in texture space */ + GLfloat d = -B*B+4.0*C*A; + GLfloat box_u = 2.0 / d * sqrt(d*C*F); /* box_u -> half of bbox with */ + GLfloat box_v = 2.0 / d * sqrt(A*d*F); /* box_v -> half of bbox height */ + + GLint u0 = floor(tex_u - box_u); + GLint u1 = ceil (tex_u + box_u); + GLint v0 = floor(tex_v - box_v); + GLint v1 = ceil (tex_v + box_v); + + GLfloat num[4] = {0.0F, 0.0F, 0.0F, 0.0F}; + GLfloat newCoord[2]; + GLfloat den = 0.0F; + GLfloat ddq; + GLfloat U = u0 - tex_u; + GLint v; + + /* Scale ellipse formula to directly index the Filter Lookup Table. + * i.e. scale so that F = WEIGHT_LUT_SIZE-1 + */ + double formScale = (double) (WEIGHT_LUT_SIZE - 1) / F; + A *= formScale; + B *= formScale; + C *= formScale; + /* F *= formScale; */ /* no need to scale F as we don't use it below here */ + + /* Heckbert MS thesis, p. 59; scan over the bounding box of the ellipse + * and incrementally update the value of Ax^2+Bxy*Cy^2; when this + * value, q, is less than F, we're inside the ellipse + */ + ddq = 2 * A; + for (v = v0; v <= v1; ++v) { + GLfloat V = v - tex_v; + GLfloat dq = A * (2 * U + 1) + B * V; + GLfloat q = (C * V + B * U) * V + A * U * U; + + GLint u; + for (u = u0; u <= u1; ++u) { + /* Note that the ellipse has been pre-scaled so F = WEIGHT_LUT_SIZE - 1 */ + if (q < WEIGHT_LUT_SIZE) { + /* as a LUT is used, q must never be negative; + * should not happen, though + */ + const GLint qClamped = q >= 0.0F ? q : 0; + GLfloat weight = weightLut[qClamped]; + + newCoord[0] = u / ((GLfloat) img->Width2); + newCoord[1] = v / ((GLfloat) img->Height2); + + sample_2d_nearest(ctx, tObj, img, newCoord, rgba); + num[0] += weight * rgba[0]; + num[1] += weight * rgba[1]; + num[2] += weight * rgba[2]; + num[3] += weight * rgba[3]; + + den += weight; + } + q += dq; + dq += ddq; + } + } + + if (den <= 0.0F) { + /* Reaching this place would mean + * that no pixels intersected the ellipse. + * This should never happen because + * the filter we use always + * intersects at least one pixel. + */ + + /*rgba[0]=0; + rgba[1]=0; + rgba[2]=0; + rgba[3]=0;*/ + /* not enough pixels in resampling, resort to direct interpolation */ + sample_2d_linear(ctx, tObj, img, texcoord, rgba); + return; + } + + rgba[0] = num[0] / den; + rgba[1] = num[1] / den; + rgba[2] = num[2] / den; + rgba[3] = num[3] / den; +} + + +/** + * Anisotropic filtering using footprint assembly as outlined in the + * EXT_texture_filter_anisotropic spec: + * http://www.opengl.org/registry/specs/EXT/texture_filter_anisotropic.txt + * Faster than EWA but has less quality (more aliasing effects) + */ +static void +sample_2d_footprint(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const GLfloat texcoord[4], + const GLfloat dudx, const GLfloat dvdx, + const GLfloat dudy, const GLfloat dvdy, const GLint lod, + GLfloat rgba[]) +{ + GLint level = lod > 0 ? lod : 0; + GLfloat scaling = 1.0F / (1 << level); + const struct gl_texture_image *img = tObj->Image[0][level]; + + GLfloat ux = dudx * scaling; + GLfloat vx = dvdx * scaling; + GLfloat uy = dudy * scaling; + GLfloat vy = dvdy * scaling; + + GLfloat Px2 = ux * ux + vx * vx; /* squared length of dx */ + GLfloat Py2 = uy * uy + vy * vy; /* squared length of dy */ + + GLint numSamples; + GLfloat ds; + GLfloat dt; + + GLfloat num[4] = {0.0F, 0.0F, 0.0F, 0.0F}; + GLfloat newCoord[2]; + GLint s; + + /* Calculate the per anisotropic sample offsets in s,t space. */ + if (Px2 > Py2) { + numSamples = ceil(SQRTF(Px2)); + ds = ux / ((GLfloat) img->Width2); + dt = vx / ((GLfloat) img->Height2); + } + else { + numSamples = ceil(SQRTF(Py2)); + ds = uy / ((GLfloat) img->Width2); + dt = vy / ((GLfloat) img->Height2); + } + + for (s = 0; s<numSamples; s++) { + newCoord[0] = texcoord[0] + ds * ((GLfloat)(s+1) / (numSamples+1) -0.5); + newCoord[1] = texcoord[1] + dt * ((GLfloat)(s+1) / (numSamples+1) -0.5); + + sample_2d_linear(ctx, tObj, img, newCoord, rgba); + num[0] += rgba[0]; + num[1] += rgba[1]; + num[2] += rgba[2]; + num[3] += rgba[3]; + } + + rgba[0] = num[0] / numSamples; + rgba[1] = num[1] / numSamples; + rgba[2] = num[2] / numSamples; + rgba[3] = num[3] / numSamples; +} + + +/** + * Returns the index of the specified texture object in the + * gl_context texture unit array. + */ +static INLINE GLuint +texture_unit_index(const struct gl_context *ctx, + const struct gl_texture_object *tObj) +{ + const GLuint maxUnit + = (ctx->Texture._EnabledCoordUnits > 1) ? ctx->Const.MaxTextureUnits : 1; + GLuint u; + + /* XXX CoordUnits vs. ImageUnits */ + for (u = 0; u < maxUnit; u++) { + if (ctx->Texture.Unit[u]._Current == tObj) + break; /* found */ + } + if (u >= maxUnit) + u = 0; /* not found, use 1st one; should never happen */ + + return u; +} + + +/** + * Sample 2D texture using an anisotropic filter. + * NOTE: the const GLfloat lambda_iso[] parameter does *NOT* contain + * the lambda float array but a "hidden" SWspan struct which is required + * by this function but is not available in the texture_sample_func signature. + * See _swrast_texture_span( struct gl_context *ctx, SWspan *span ) on how + * this function is called. + */ +static void +sample_lambda_2d_aniso(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoords[][4], + const GLfloat lambda_iso[], GLfloat rgba[][4]) +{ + const struct gl_texture_image *tImg = tObj->Image[0][tObj->BaseLevel]; + const GLfloat maxEccentricity = + tObj->Sampler.MaxAnisotropy * tObj->Sampler.MaxAnisotropy; + + /* re-calculate the lambda values so that they are usable with anisotropic + * filtering + */ + SWspan *span = (SWspan *)lambda_iso; /* access the "hidden" SWspan struct */ + + /* based on interpolate_texcoords(struct gl_context *ctx, SWspan *span) + * in swrast/s_span.c + */ + + /* find the texture unit index by looking up the current texture object + * from the context list of available texture objects. + */ + const GLuint u = texture_unit_index(ctx, tObj); + const GLuint attr = FRAG_ATTRIB_TEX0 + u; + GLfloat texW, texH; + + const GLfloat dsdx = span->attrStepX[attr][0]; + const GLfloat dsdy = span->attrStepY[attr][0]; + const GLfloat dtdx = span->attrStepX[attr][1]; + const GLfloat dtdy = span->attrStepY[attr][1]; + const GLfloat dqdx = span->attrStepX[attr][3]; + const GLfloat dqdy = span->attrStepY[attr][3]; + GLfloat s = span->attrStart[attr][0] + span->leftClip * dsdx; + GLfloat t = span->attrStart[attr][1] + span->leftClip * dtdx; + GLfloat q = span->attrStart[attr][3] + span->leftClip * dqdx; + + /* from swrast/s_texcombine.c _swrast_texture_span */ + const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[u]; + const GLboolean adjustLOD = + (texUnit->LodBias + tObj->Sampler.LodBias != 0.0F) + || (tObj->Sampler.MinLod != -1000.0 || tObj->Sampler.MaxLod != 1000.0); + + GLuint i; + + /* on first access create the lookup table containing the filter weights. */ + if (!weightLut) { + create_filter_table(); + } + + texW = tImg->WidthScale; + texH = tImg->HeightScale; + + for (i = 0; i < n; i++) { + const GLfloat invQ = (q == 0.0F) ? 1.0F : (1.0F / q); + + GLfloat dudx = texW * ((s + dsdx) / (q + dqdx) - s * invQ); + GLfloat dvdx = texH * ((t + dtdx) / (q + dqdx) - t * invQ); + GLfloat dudy = texW * ((s + dsdy) / (q + dqdy) - s * invQ); + GLfloat dvdy = texH * ((t + dtdy) / (q + dqdy) - t * invQ); + + /* note: instead of working with Px and Py, we will use the + * squared length instead, to avoid sqrt. + */ + GLfloat Px2 = dudx * dudx + dvdx * dvdx; + GLfloat Py2 = dudy * dudy + dvdy * dvdy; + + GLfloat Pmax2; + GLfloat Pmin2; + GLfloat e; + GLfloat lod; + + s += dsdx; + t += dtdx; + q += dqdx; + + if (Px2 < Py2) { + Pmax2 = Py2; + Pmin2 = Px2; + } + else { + Pmax2 = Px2; + Pmin2 = Py2; + } + + /* if the eccentricity of the ellipse is too big, scale up the shorter + * of the two vectors to limit the maximum amount of work per pixel + */ + e = Pmax2 / Pmin2; + if (e > maxEccentricity) { + /* GLfloat s=e / maxEccentricity; + minor[0] *= s; + minor[1] *= s; + Pmin2 *= s; */ + Pmin2 = Pmax2 / maxEccentricity; + } + + /* note: we need to have Pmin=sqrt(Pmin2) here, but we can avoid + * this since 0.5*log(x) = log(sqrt(x)) + */ + lod = 0.5 * LOG2(Pmin2); + + if (adjustLOD) { + /* from swrast/s_texcombine.c _swrast_texture_span */ + if (texUnit->LodBias + tObj->Sampler.LodBias != 0.0F) { + /* apply LOD bias, but don't clamp yet */ + const GLfloat bias = + CLAMP(texUnit->LodBias + tObj->Sampler.LodBias, + -ctx->Const.MaxTextureLodBias, + ctx->Const.MaxTextureLodBias); + lod += bias; + + if (tObj->Sampler.MinLod != -1000.0 || + tObj->Sampler.MaxLod != 1000.0) { + /* apply LOD clamping to lambda */ + lod = CLAMP(lod, tObj->Sampler.MinLod, tObj->Sampler.MaxLod); + } + } + } + + /* If the ellipse covers the whole image, we can + * simply return the average of the whole image. + */ + if (lod >= tObj->_MaxLevel) { + sample_2d_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoords[i], rgba[i]); + } + else { + /* don't bother interpolating between multiple LODs; it doesn't + * seem to be worth the extra running time. + */ + sample_2d_ewa(ctx, tObj, texcoords[i], + dudx, dvdx, dudy, dvdy, floor(lod), rgba[i]); + + /* unused: */ + (void) sample_2d_footprint; + /* + sample_2d_footprint(ctx, tObj, texcoords[i], + dudx, dvdx, dudy, dvdy, floor(lod), rgba[i]); + */ + } + } +} + + + +/**********************************************************************/ +/* 3-D Texture Sampling Functions */ +/**********************************************************************/ + +/** + * Return the texture sample for coordinate (s,t,r) using GL_NEAREST filter. + */ +static INLINE void +sample_3d_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], + GLfloat rgba[4]) +{ + const GLint width = img->Width2; /* without border, power of two */ + const GLint height = img->Height2; /* without border, power of two */ + const GLint depth = img->Depth2; /* without border, power of two */ + GLint i, j, k; + (void) ctx; + + i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]); + j = nearest_texel_location(tObj->Sampler.WrapT, img, height, texcoord[1]); + k = nearest_texel_location(tObj->Sampler.WrapR, img, depth, texcoord[2]); + + if (i < 0 || i >= (GLint) img->Width || + j < 0 || j >= (GLint) img->Height || + k < 0 || k >= (GLint) img->Depth) { + /* Need this test for GL_CLAMP_TO_BORDER mode */ + get_border_color(tObj, img, rgba); + } + else { + img->FetchTexelf(img, i, j, k, rgba); + } +} + + +/** + * Return the texture sample for coordinate (s,t,r) using GL_LINEAR filter. + */ +static void +sample_3d_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], + GLfloat rgba[4]) +{ + const GLint width = img->Width2; + const GLint height = img->Height2; + const GLint depth = img->Depth2; + GLint i0, j0, k0, i1, j1, k1; + GLbitfield useBorderColor = 0x0; + GLfloat a, b, c; + GLfloat t000[4], t010[4], t001[4], t011[4]; + GLfloat t100[4], t110[4], t101[4], t111[4]; + + linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a); + linear_texel_locations(tObj->Sampler.WrapT, img, height, texcoord[1], &j0, &j1, &b); + linear_texel_locations(tObj->Sampler.WrapR, img, depth, texcoord[2], &k0, &k1, &c); + + if (img->Border) { + i0 += img->Border; + i1 += img->Border; + j0 += img->Border; + j1 += img->Border; + k0 += img->Border; + k1 += img->Border; + } + else { + /* check if sampling texture border color */ + if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT; + if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT; + if (j0 < 0 || j0 >= height) useBorderColor |= J0BIT; + if (j1 < 0 || j1 >= height) useBorderColor |= J1BIT; + if (k0 < 0 || k0 >= depth) useBorderColor |= K0BIT; + if (k1 < 0 || k1 >= depth) useBorderColor |= K1BIT; + } + + /* Fetch texels */ + if (useBorderColor & (I0BIT | J0BIT | K0BIT)) { + get_border_color(tObj, img, t000); + } + else { + img->FetchTexelf(img, i0, j0, k0, t000); + } + if (useBorderColor & (I1BIT | J0BIT | K0BIT)) { + get_border_color(tObj, img, t100); + } + else { + img->FetchTexelf(img, i1, j0, k0, t100); + } + if (useBorderColor & (I0BIT | J1BIT | K0BIT)) { + get_border_color(tObj, img, t010); + } + else { + img->FetchTexelf(img, i0, j1, k0, t010); + } + if (useBorderColor & (I1BIT | J1BIT | K0BIT)) { + get_border_color(tObj, img, t110); + } + else { + img->FetchTexelf(img, i1, j1, k0, t110); + } + + if (useBorderColor & (I0BIT | J0BIT | K1BIT)) { + get_border_color(tObj, img, t001); + } + else { + img->FetchTexelf(img, i0, j0, k1, t001); + } + if (useBorderColor & (I1BIT | J0BIT | K1BIT)) { + get_border_color(tObj, img, t101); + } + else { + img->FetchTexelf(img, i1, j0, k1, t101); + } + if (useBorderColor & (I0BIT | J1BIT | K1BIT)) { + get_border_color(tObj, img, t011); + } + else { + img->FetchTexelf(img, i0, j1, k1, t011); + } + if (useBorderColor & (I1BIT | J1BIT | K1BIT)) { + get_border_color(tObj, img, t111); + } + else { + img->FetchTexelf(img, i1, j1, k1, t111); + } + + /* trilinear interpolation of samples */ + lerp_rgba_3d(rgba, a, b, c, t000, t100, t010, t110, t001, t101, t011, t111); +} + + +static void +sample_3d_nearest_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4] ) +{ + GLuint i; + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_3d_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]); + } +} + + +static void +sample_3d_linear_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_3d_linear(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]); + } +} + + +static void +sample_3d_nearest_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_3d_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_3d_nearest(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0); + sample_3d_nearest(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +static void +sample_3d_linear_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_3d_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_3d_linear(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0); + sample_3d_linear(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +/** Sample 3D texture, nearest filtering for both min/magnification */ +static void +sample_nearest_3d(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + for (i = 0; i < n; i++) { + sample_3d_nearest(ctx, tObj, image, texcoords[i], rgba[i]); + } +} + + +/** Sample 3D texture, linear filtering for both min/magnification */ +static void +sample_linear_3d(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + for (i = 0; i < n; i++) { + sample_3d_linear(ctx, tObj, image, texcoords[i], rgba[i]); + } +} + + +/** Sample 3D texture, using lambda to choose between min/magnification */ +static void +sample_lambda_3d(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + GLuint minStart, minEnd; /* texels with minification */ + GLuint magStart, magEnd; /* texels with magnification */ + GLuint i; + + ASSERT(lambda != NULL); + compute_min_mag_ranges(tObj, n, lambda, + &minStart, &minEnd, &magStart, &magEnd); + + if (minStart < minEnd) { + /* do the minified texels */ + GLuint m = minEnd - minStart; + switch (tObj->Sampler.MinFilter) { + case GL_NEAREST: + for (i = minStart; i < minEnd; i++) + sample_3d_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_LINEAR: + for (i = minStart; i < minEnd; i++) + sample_3d_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_NEAREST_MIPMAP_NEAREST: + sample_3d_nearest_mipmap_nearest(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_NEAREST: + sample_3d_linear_mipmap_nearest(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_NEAREST_MIPMAP_LINEAR: + sample_3d_nearest_mipmap_linear(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_LINEAR: + sample_3d_linear_mipmap_linear(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + default: + _mesa_problem(ctx, "Bad min filter in sample_3d_texture"); + return; + } + } + + if (magStart < magEnd) { + /* do the magnified texels */ + switch (tObj->Sampler.MagFilter) { + case GL_NEAREST: + for (i = magStart; i < magEnd; i++) + sample_3d_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_LINEAR: + for (i = magStart; i < magEnd; i++) + sample_3d_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + default: + _mesa_problem(ctx, "Bad mag filter in sample_3d_texture"); + return; + } + } +} + + +/**********************************************************************/ +/* Texture Cube Map Sampling Functions */ +/**********************************************************************/ + +/** + * Choose one of six sides of a texture cube map given the texture + * coord (rx,ry,rz). Return pointer to corresponding array of texture + * images. + */ +static const struct gl_texture_image ** +choose_cube_face(const struct gl_texture_object *texObj, + const GLfloat texcoord[4], GLfloat newCoord[4]) +{ + /* + major axis + direction target sc tc ma + ---------- ------------------------------- --- --- --- + +rx TEXTURE_CUBE_MAP_POSITIVE_X_EXT -rz -ry rx + -rx TEXTURE_CUBE_MAP_NEGATIVE_X_EXT +rz -ry rx + +ry TEXTURE_CUBE_MAP_POSITIVE_Y_EXT +rx +rz ry + -ry TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT +rx -rz ry + +rz TEXTURE_CUBE_MAP_POSITIVE_Z_EXT +rx -ry rz + -rz TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT -rx -ry rz + */ + const GLfloat rx = texcoord[0]; + const GLfloat ry = texcoord[1]; + const GLfloat rz = texcoord[2]; + const GLfloat arx = FABSF(rx), ary = FABSF(ry), arz = FABSF(rz); + GLuint face; + GLfloat sc, tc, ma; + + if (arx >= ary && arx >= arz) { + if (rx >= 0.0F) { + face = FACE_POS_X; + sc = -rz; + tc = -ry; + ma = arx; + } + else { + face = FACE_NEG_X; + sc = rz; + tc = -ry; + ma = arx; + } + } + else if (ary >= arx && ary >= arz) { + if (ry >= 0.0F) { + face = FACE_POS_Y; + sc = rx; + tc = rz; + ma = ary; + } + else { + face = FACE_NEG_Y; + sc = rx; + tc = -rz; + ma = ary; + } + } + else { + if (rz > 0.0F) { + face = FACE_POS_Z; + sc = rx; + tc = -ry; + ma = arz; + } + else { + face = FACE_NEG_Z; + sc = -rx; + tc = -ry; + ma = arz; + } + } + + { + const float ima = 1.0F / ma; + newCoord[0] = ( sc * ima + 1.0F ) * 0.5F; + newCoord[1] = ( tc * ima + 1.0F ) * 0.5F; + } + + return (const struct gl_texture_image **) texObj->Image[face]; +} + + +static void +sample_nearest_cube(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + GLuint i; + (void) lambda; + for (i = 0; i < n; i++) { + const struct gl_texture_image **images; + GLfloat newCoord[4]; + images = choose_cube_face(tObj, texcoords[i], newCoord); + sample_2d_nearest(ctx, tObj, images[tObj->BaseLevel], + newCoord, rgba[i]); + } +} + + +static void +sample_linear_cube(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + (void) lambda; + for (i = 0; i < n; i++) { + const struct gl_texture_image **images; + GLfloat newCoord[4]; + images = choose_cube_face(tObj, texcoords[i], newCoord); + sample_2d_linear(ctx, tObj, images[tObj->BaseLevel], + newCoord, rgba[i]); + } +} + + +static void +sample_cube_nearest_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + const struct gl_texture_image **images; + GLfloat newCoord[4]; + GLint level; + images = choose_cube_face(tObj, texcoord[i], newCoord); + + /* XXX we actually need to recompute lambda here based on the newCoords. + * But we would need the texcoords of adjacent fragments to compute that + * properly, and we don't have those here. + * For now, do an approximation: subtracting 1 from the chosen mipmap + * level seems to work in some test cases. + * The same adjustment is done in the next few functions. + */ + level = nearest_mipmap_level(tObj, lambda[i]); + level = MAX2(level - 1, 0); + + sample_2d_nearest(ctx, tObj, images[level], newCoord, rgba[i]); + } +} + + +static void +sample_cube_linear_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + const struct gl_texture_image **images; + GLfloat newCoord[4]; + GLint level = nearest_mipmap_level(tObj, lambda[i]); + level = MAX2(level - 1, 0); /* see comment above */ + images = choose_cube_face(tObj, texcoord[i], newCoord); + sample_2d_linear(ctx, tObj, images[level], newCoord, rgba[i]); + } +} + + +static void +sample_cube_nearest_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + const struct gl_texture_image **images; + GLfloat newCoord[4]; + GLint level = linear_mipmap_level(tObj, lambda[i]); + level = MAX2(level - 1, 0); /* see comment above */ + images = choose_cube_face(tObj, texcoord[i], newCoord); + if (level >= tObj->_MaxLevel) { + sample_2d_nearest(ctx, tObj, images[tObj->_MaxLevel], + newCoord, rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_2d_nearest(ctx, tObj, images[level ], newCoord, t0); + sample_2d_nearest(ctx, tObj, images[level+1], newCoord, t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +static void +sample_cube_linear_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + const struct gl_texture_image **images; + GLfloat newCoord[4]; + GLint level = linear_mipmap_level(tObj, lambda[i]); + level = MAX2(level - 1, 0); /* see comment above */ + images = choose_cube_face(tObj, texcoord[i], newCoord); + if (level >= tObj->_MaxLevel) { + sample_2d_linear(ctx, tObj, images[tObj->_MaxLevel], + newCoord, rgba[i]); + } + else { + GLfloat t0[4], t1[4]; + const GLfloat f = FRAC(lambda[i]); + sample_2d_linear(ctx, tObj, images[level ], newCoord, t0); + sample_2d_linear(ctx, tObj, images[level+1], newCoord, t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +/** Sample cube texture, using lambda to choose between min/magnification */ +static void +sample_lambda_cube(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + GLuint minStart, minEnd; /* texels with minification */ + GLuint magStart, magEnd; /* texels with magnification */ + + ASSERT(lambda != NULL); + compute_min_mag_ranges(tObj, n, lambda, + &minStart, &minEnd, &magStart, &magEnd); + + if (minStart < minEnd) { + /* do the minified texels */ + const GLuint m = minEnd - minStart; + switch (tObj->Sampler.MinFilter) { + case GL_NEAREST: + sample_nearest_cube(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR: + sample_linear_cube(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_NEAREST_MIPMAP_NEAREST: + sample_cube_nearest_mipmap_nearest(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_NEAREST: + sample_cube_linear_mipmap_nearest(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_NEAREST_MIPMAP_LINEAR: + sample_cube_nearest_mipmap_linear(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_LINEAR: + sample_cube_linear_mipmap_linear(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + default: + _mesa_problem(ctx, "Bad min filter in sample_lambda_cube"); + } + } + + if (magStart < magEnd) { + /* do the magnified texels */ + const GLuint m = magEnd - magStart; + switch (tObj->Sampler.MagFilter) { + case GL_NEAREST: + sample_nearest_cube(ctx, tObj, m, texcoords + magStart, + lambda + magStart, rgba + magStart); + break; + case GL_LINEAR: + sample_linear_cube(ctx, tObj, m, texcoords + magStart, + lambda + magStart, rgba + magStart); + break; + default: + _mesa_problem(ctx, "Bad mag filter in sample_lambda_cube"); + } + } +} + + +/**********************************************************************/ +/* Texture Rectangle Sampling Functions */ +/**********************************************************************/ + + +static void +sample_nearest_rect(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + const struct gl_texture_image *img = tObj->Image[0][0]; + const GLint width = img->Width; + const GLint height = img->Height; + GLuint i; + + (void) ctx; + (void) lambda; + + ASSERT(tObj->Sampler.WrapS == GL_CLAMP || + tObj->Sampler.WrapS == GL_CLAMP_TO_EDGE || + tObj->Sampler.WrapS == GL_CLAMP_TO_BORDER); + ASSERT(tObj->Sampler.WrapT == GL_CLAMP || + tObj->Sampler.WrapT == GL_CLAMP_TO_EDGE || + tObj->Sampler.WrapT == GL_CLAMP_TO_BORDER); + + for (i = 0; i < n; i++) { + GLint row, col; + col = clamp_rect_coord_nearest(tObj->Sampler.WrapS, texcoords[i][0], width); + row = clamp_rect_coord_nearest(tObj->Sampler.WrapT, texcoords[i][1], height); + if (col < 0 || col >= width || row < 0 || row >= height) + get_border_color(tObj, img, rgba[i]); + else + img->FetchTexelf(img, col, row, 0, rgba[i]); + } +} + + +static void +sample_linear_rect(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + const struct gl_texture_image *img = tObj->Image[0][0]; + const GLint width = img->Width; + const GLint height = img->Height; + GLuint i; + + (void) ctx; + (void) lambda; + + ASSERT(tObj->Sampler.WrapS == GL_CLAMP || + tObj->Sampler.WrapS == GL_CLAMP_TO_EDGE || + tObj->Sampler.WrapS == GL_CLAMP_TO_BORDER); + ASSERT(tObj->Sampler.WrapT == GL_CLAMP || + tObj->Sampler.WrapT == GL_CLAMP_TO_EDGE || + tObj->Sampler.WrapT == GL_CLAMP_TO_BORDER); + + for (i = 0; i < n; i++) { + GLint i0, j0, i1, j1; + GLfloat t00[4], t01[4], t10[4], t11[4]; + GLfloat a, b; + GLbitfield useBorderColor = 0x0; + + clamp_rect_coord_linear(tObj->Sampler.WrapS, texcoords[i][0], width, + &i0, &i1, &a); + clamp_rect_coord_linear(tObj->Sampler.WrapT, texcoords[i][1], height, + &j0, &j1, &b); + + /* compute integer rows/columns */ + if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT; + if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT; + if (j0 < 0 || j0 >= height) useBorderColor |= J0BIT; + if (j1 < 0 || j1 >= height) useBorderColor |= J1BIT; + + /* get four texel samples */ + if (useBorderColor & (I0BIT | J0BIT)) + get_border_color(tObj, img, t00); + else + img->FetchTexelf(img, i0, j0, 0, t00); + + if (useBorderColor & (I1BIT | J0BIT)) + get_border_color(tObj, img, t10); + else + img->FetchTexelf(img, i1, j0, 0, t10); + + if (useBorderColor & (I0BIT | J1BIT)) + get_border_color(tObj, img, t01); + else + img->FetchTexelf(img, i0, j1, 0, t01); + + if (useBorderColor & (I1BIT | J1BIT)) + get_border_color(tObj, img, t11); + else + img->FetchTexelf(img, i1, j1, 0, t11); + + lerp_rgba_2d(rgba[i], a, b, t00, t10, t01, t11); + } +} + + +/** Sample Rect texture, using lambda to choose between min/magnification */ +static void +sample_lambda_rect(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat 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(tObj, n, lambda, + &minStart, &minEnd, &magStart, &magEnd); + + if (minStart < minEnd) { + if (tObj->Sampler.MinFilter == GL_NEAREST) { + sample_nearest_rect(ctx, tObj, minEnd - minStart, + texcoords + minStart, NULL, rgba + minStart); + } + else { + sample_linear_rect(ctx, tObj, minEnd - minStart, + texcoords + minStart, NULL, rgba + minStart); + } + } + if (magStart < magEnd) { + if (tObj->Sampler.MagFilter == GL_NEAREST) { + sample_nearest_rect(ctx, tObj, magEnd - magStart, + texcoords + magStart, NULL, rgba + magStart); + } + else { + sample_linear_rect(ctx, tObj, magEnd - magStart, + texcoords + magStart, NULL, rgba + magStart); + } + } +} + + +/**********************************************************************/ +/* 2D Texture Array Sampling Functions */ +/**********************************************************************/ + +/** + * Return the texture sample for coordinate (s,t,r) using GL_NEAREST filter. + */ +static void +sample_2d_array_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], + GLfloat rgba[4]) +{ + const GLint width = img->Width2; /* without border, power of two */ + const GLint height = img->Height2; /* without border, power of two */ + const GLint depth = img->Depth; + GLint i, j; + GLint array; + (void) ctx; + + i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]); + j = nearest_texel_location(tObj->Sampler.WrapT, img, height, texcoord[1]); + array = tex_array_slice(texcoord[2], depth); + + if (i < 0 || i >= (GLint) img->Width || + j < 0 || j >= (GLint) img->Height || + array < 0 || array >= (GLint) img->Depth) { + /* Need this test for GL_CLAMP_TO_BORDER mode */ + get_border_color(tObj, img, rgba); + } + else { + img->FetchTexelf(img, i, j, array, rgba); + } +} + + +/** + * Return the texture sample for coordinate (s,t,r) using GL_LINEAR filter. + */ +static void +sample_2d_array_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], + GLfloat rgba[4]) +{ + const GLint width = img->Width2; + const GLint height = img->Height2; + const GLint depth = img->Depth; + GLint i0, j0, i1, j1; + GLint array; + GLbitfield useBorderColor = 0x0; + GLfloat a, b; + GLfloat t00[4], t01[4], t10[4], t11[4]; + + linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a); + linear_texel_locations(tObj->Sampler.WrapT, img, height, texcoord[1], &j0, &j1, &b); + array = tex_array_slice(texcoord[2], depth); + + if (array < 0 || array >= depth) { + COPY_4V(rgba, tObj->Sampler.BorderColor.f); + } + else { + if (img->Border) { + i0 += img->Border; + i1 += img->Border; + j0 += img->Border; + j1 += img->Border; + } + else { + /* check if sampling texture border color */ + if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT; + if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT; + if (j0 < 0 || j0 >= height) useBorderColor |= J0BIT; + if (j1 < 0 || j1 >= height) useBorderColor |= J1BIT; + } + + /* Fetch texels */ + if (useBorderColor & (I0BIT | J0BIT)) { + get_border_color(tObj, img, t00); + } + else { + img->FetchTexelf(img, i0, j0, array, t00); + } + if (useBorderColor & (I1BIT | J0BIT)) { + get_border_color(tObj, img, t10); + } + else { + img->FetchTexelf(img, i1, j0, array, t10); + } + if (useBorderColor & (I0BIT | J1BIT)) { + get_border_color(tObj, img, t01); + } + else { + img->FetchTexelf(img, i0, j1, array, t01); + } + if (useBorderColor & (I1BIT | J1BIT)) { + get_border_color(tObj, img, t11); + } + else { + img->FetchTexelf(img, i1, j1, array, t11); + } + + /* trilinear interpolation of samples */ + lerp_rgba_2d(rgba, a, b, t00, t10, t01, t11); + } +} + + +static void +sample_2d_array_nearest_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_2d_array_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i], + rgba[i]); + } +} + + +static void +sample_2d_array_linear_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_2d_array_linear(ctx, tObj, tObj->Image[0][level], + texcoord[i], rgba[i]); + } +} + + +static void +sample_2d_array_nearest_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_2d_array_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_2d_array_nearest(ctx, tObj, tObj->Image[0][level ], + texcoord[i], t0); + sample_2d_array_nearest(ctx, tObj, tObj->Image[0][level+1], + texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +static void +sample_2d_array_linear_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_2d_array_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_2d_array_linear(ctx, tObj, tObj->Image[0][level ], + texcoord[i], t0); + sample_2d_array_linear(ctx, tObj, tObj->Image[0][level+1], + texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +/** Sample 2D Array texture, nearest filtering for both min/magnification */ +static void +sample_nearest_2d_array(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + for (i = 0; i < n; i++) { + sample_2d_array_nearest(ctx, tObj, image, texcoords[i], rgba[i]); + } +} + + + +/** Sample 2D Array texture, linear filtering for both min/magnification */ +static void +sample_linear_2d_array(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + for (i = 0; i < n; i++) { + sample_2d_array_linear(ctx, tObj, image, texcoords[i], rgba[i]); + } +} + + +/** Sample 2D Array texture, using lambda to choose between min/magnification */ +static void +sample_lambda_2d_array(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + GLuint minStart, minEnd; /* texels with minification */ + GLuint magStart, magEnd; /* texels with magnification */ + GLuint i; + + ASSERT(lambda != NULL); + compute_min_mag_ranges(tObj, n, lambda, + &minStart, &minEnd, &magStart, &magEnd); + + if (minStart < minEnd) { + /* do the minified texels */ + GLuint m = minEnd - minStart; + switch (tObj->Sampler.MinFilter) { + case GL_NEAREST: + for (i = minStart; i < minEnd; i++) + sample_2d_array_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_LINEAR: + for (i = minStart; i < minEnd; i++) + sample_2d_array_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_NEAREST_MIPMAP_NEAREST: + sample_2d_array_nearest_mipmap_nearest(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, + rgba + minStart); + break; + case GL_LINEAR_MIPMAP_NEAREST: + sample_2d_array_linear_mipmap_nearest(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, + rgba + minStart); + break; + case GL_NEAREST_MIPMAP_LINEAR: + sample_2d_array_nearest_mipmap_linear(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, + rgba + minStart); + break; + case GL_LINEAR_MIPMAP_LINEAR: + sample_2d_array_linear_mipmap_linear(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, + rgba + minStart); + break; + default: + _mesa_problem(ctx, "Bad min filter in sample_2d_array_texture"); + return; + } + } + + if (magStart < magEnd) { + /* do the magnified texels */ + switch (tObj->Sampler.MagFilter) { + case GL_NEAREST: + for (i = magStart; i < magEnd; i++) + sample_2d_array_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_LINEAR: + for (i = magStart; i < magEnd; i++) + sample_2d_array_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + default: + _mesa_problem(ctx, "Bad mag filter in sample_2d_array_texture"); + return; + } + } +} + + + + +/**********************************************************************/ +/* 1D Texture Array Sampling Functions */ +/**********************************************************************/ + +/** + * Return the texture sample for coordinate (s,t,r) using GL_NEAREST filter. + */ +static void +sample_1d_array_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], + GLfloat rgba[4]) +{ + const GLint width = img->Width2; /* without border, power of two */ + const GLint height = img->Height; + GLint i; + GLint array; + (void) ctx; + + i = nearest_texel_location(tObj->Sampler.WrapS, img, width, texcoord[0]); + array = tex_array_slice(texcoord[1], height); + + if (i < 0 || i >= (GLint) img->Width || + array < 0 || array >= (GLint) img->Height) { + /* Need this test for GL_CLAMP_TO_BORDER mode */ + get_border_color(tObj, img, rgba); + } + else { + img->FetchTexelf(img, i, array, 0, rgba); + } +} + + +/** + * Return the texture sample for coordinate (s,t,r) using GL_LINEAR filter. + */ +static void +sample_1d_array_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + const struct gl_texture_image *img, + const GLfloat texcoord[4], + GLfloat rgba[4]) +{ + const GLint width = img->Width2; + const GLint height = img->Height; + GLint i0, i1; + GLint array; + GLbitfield useBorderColor = 0x0; + GLfloat a; + GLfloat t0[4], t1[4]; + + linear_texel_locations(tObj->Sampler.WrapS, img, width, texcoord[0], &i0, &i1, &a); + array = tex_array_slice(texcoord[1], height); + + if (img->Border) { + i0 += img->Border; + i1 += img->Border; + } + else { + /* check if sampling texture border color */ + if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT; + if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT; + } + + if (array < 0 || array >= height) useBorderColor |= K0BIT; + + /* Fetch texels */ + if (useBorderColor & (I0BIT | K0BIT)) { + get_border_color(tObj, img, t0); + } + else { + img->FetchTexelf(img, i0, array, 0, t0); + } + if (useBorderColor & (I1BIT | K0BIT)) { + get_border_color(tObj, img, t1); + } + else { + img->FetchTexelf(img, i1, array, 0, t1); + } + + /* bilinear interpolation of samples */ + lerp_rgba(rgba, a, t0, t1); +} + + +static void +sample_1d_array_nearest_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_1d_array_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i], + rgba[i]); + } +} + + +static void +sample_1d_array_linear_mipmap_nearest(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = nearest_mipmap_level(tObj, lambda[i]); + sample_1d_array_linear(ctx, tObj, tObj->Image[0][level], + texcoord[i], rgba[i]); + } +} + + +static void +sample_1d_array_nearest_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_1d_array_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_1d_array_nearest(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0); + sample_1d_array_nearest(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +static void +sample_1d_array_linear_mipmap_linear(struct gl_context *ctx, + const struct gl_texture_object *tObj, + GLuint n, const GLfloat texcoord[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + ASSERT(lambda != NULL); + for (i = 0; i < n; i++) { + GLint level = linear_mipmap_level(tObj, lambda[i]); + if (level >= tObj->_MaxLevel) { + sample_1d_array_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel], + texcoord[i], rgba[i]); + } + else { + GLfloat t0[4], t1[4]; /* texels */ + const GLfloat f = FRAC(lambda[i]); + sample_1d_array_linear(ctx, tObj, tObj->Image[0][level ], texcoord[i], t0); + sample_1d_array_linear(ctx, tObj, tObj->Image[0][level+1], texcoord[i], t1); + lerp_rgba(rgba[i], f, t0, t1); + } + } +} + + +/** Sample 1D Array texture, nearest filtering for both min/magnification */ +static void +sample_nearest_1d_array(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + for (i = 0; i < n; i++) { + sample_1d_array_nearest(ctx, tObj, image, texcoords[i], rgba[i]); + } +} + + +/** Sample 1D Array texture, linear filtering for both min/magnification */ +static void +sample_linear_1d_array(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], + const GLfloat lambda[], GLfloat rgba[][4]) +{ + GLuint i; + struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel]; + (void) lambda; + for (i = 0; i < n; i++) { + sample_1d_array_linear(ctx, tObj, image, texcoords[i], rgba[i]); + } +} + + +/** Sample 1D Array texture, using lambda to choose between min/magnification */ +static void +sample_lambda_1d_array(struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + GLuint minStart, minEnd; /* texels with minification */ + GLuint magStart, magEnd; /* texels with magnification */ + GLuint i; + + ASSERT(lambda != NULL); + compute_min_mag_ranges(tObj, n, lambda, + &minStart, &minEnd, &magStart, &magEnd); + + if (minStart < minEnd) { + /* do the minified texels */ + GLuint m = minEnd - minStart; + switch (tObj->Sampler.MinFilter) { + case GL_NEAREST: + for (i = minStart; i < minEnd; i++) + sample_1d_array_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_LINEAR: + for (i = minStart; i < minEnd; i++) + sample_1d_array_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_NEAREST_MIPMAP_NEAREST: + sample_1d_array_nearest_mipmap_nearest(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_NEAREST: + sample_1d_array_linear_mipmap_nearest(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, + rgba + minStart); + break; + case GL_NEAREST_MIPMAP_LINEAR: + sample_1d_array_nearest_mipmap_linear(ctx, tObj, m, texcoords + minStart, + lambda + minStart, rgba + minStart); + break; + case GL_LINEAR_MIPMAP_LINEAR: + sample_1d_array_linear_mipmap_linear(ctx, tObj, m, + texcoords + minStart, + lambda + minStart, + rgba + minStart); + break; + default: + _mesa_problem(ctx, "Bad min filter in sample_1d_array_texture"); + return; + } + } + + if (magStart < magEnd) { + /* do the magnified texels */ + switch (tObj->Sampler.MagFilter) { + case GL_NEAREST: + for (i = magStart; i < magEnd; i++) + sample_1d_array_nearest(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + case GL_LINEAR: + for (i = magStart; i < magEnd; i++) + sample_1d_array_linear(ctx, tObj, tObj->Image[0][tObj->BaseLevel], + texcoords[i], rgba[i]); + break; + default: + _mesa_problem(ctx, "Bad mag filter in sample_1d_array_texture"); + return; + } + } +} + + +/** + * Compare texcoord against depth sample. Return 1.0 or the ambient value. + */ +static INLINE GLfloat +shadow_compare(GLenum function, GLfloat coord, GLfloat depthSample, + GLfloat ambient) +{ + switch (function) { + case GL_LEQUAL: + return (coord <= depthSample) ? 1.0F : ambient; + case GL_GEQUAL: + return (coord >= depthSample) ? 1.0F : ambient; + case GL_LESS: + return (coord < depthSample) ? 1.0F : ambient; + case GL_GREATER: + return (coord > depthSample) ? 1.0F : ambient; + case GL_EQUAL: + return (coord == depthSample) ? 1.0F : ambient; + case GL_NOTEQUAL: + return (coord != depthSample) ? 1.0F : ambient; + case GL_ALWAYS: + return 1.0F; + case GL_NEVER: + return ambient; + case GL_NONE: + return depthSample; + default: + _mesa_problem(NULL, "Bad compare func in shadow_compare"); + return ambient; + } +} + + +/** + * Compare texcoord against four depth samples. + */ +static INLINE GLfloat +shadow_compare4(GLenum function, GLfloat coord, + GLfloat depth00, GLfloat depth01, + GLfloat depth10, GLfloat depth11, + GLfloat ambient, GLfloat wi, GLfloat wj) +{ + const GLfloat d = (1.0F - (GLfloat) ambient) * 0.25F; + GLfloat luminance = 1.0F; + + switch (function) { + case GL_LEQUAL: + if (coord > depth00) luminance -= d; + if (coord > depth01) luminance -= d; + if (coord > depth10) luminance -= d; + if (coord > depth11) luminance -= d; + return luminance; + case GL_GEQUAL: + if (coord < depth00) luminance -= d; + if (coord < depth01) luminance -= d; + if (coord < depth10) luminance -= d; + if (coord < depth11) luminance -= d; + return luminance; + case GL_LESS: + if (coord >= depth00) luminance -= d; + if (coord >= depth01) luminance -= d; + if (coord >= depth10) luminance -= d; + if (coord >= depth11) luminance -= d; + return luminance; + case GL_GREATER: + if (coord <= depth00) luminance -= d; + if (coord <= depth01) luminance -= d; + if (coord <= depth10) luminance -= d; + if (coord <= depth11) luminance -= d; + return luminance; + case GL_EQUAL: + if (coord != depth00) luminance -= d; + if (coord != depth01) luminance -= d; + if (coord != depth10) luminance -= d; + if (coord != depth11) luminance -= d; + return luminance; + case GL_NOTEQUAL: + if (coord == depth00) luminance -= d; + if (coord == depth01) luminance -= d; + if (coord == depth10) luminance -= d; + if (coord == depth11) luminance -= d; + return luminance; + case GL_ALWAYS: + return 1.0F; + case GL_NEVER: + return ambient; + case GL_NONE: + /* ordinary bilinear filtering */ + return lerp_2d(wi, wj, depth00, depth10, depth01, depth11); + default: + _mesa_problem(NULL, "Bad compare func in sample_compare4"); + return ambient; + } +} + + +/** + * Choose the mipmap level to use when sampling from a depth texture. + */ +static int +choose_depth_texture_level(const struct gl_texture_object *tObj, GLfloat lambda) +{ + GLint level; + + if (tObj->Sampler.MinFilter == GL_NEAREST || tObj->Sampler.MinFilter == GL_LINEAR) { + /* no mipmapping - use base level */ + level = tObj->BaseLevel; + } + else { + /* choose mipmap level */ + lambda = CLAMP(lambda, tObj->Sampler.MinLod, tObj->Sampler.MaxLod); + level = (GLint) lambda; + level = CLAMP(level, tObj->BaseLevel, tObj->_MaxLevel); + } + + return level; +} + + +/** + * Sample a shadow/depth texture. This function is incomplete. It doesn't + * check for minification vs. magnification, etc. + */ +static void +sample_depth_texture( struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat texel[][4] ) +{ + const GLint level = choose_depth_texture_level(tObj, lambda[0]); + const struct gl_texture_image *img = tObj->Image[0][level]; + const GLint width = img->Width; + const GLint height = img->Height; + const GLint depth = img->Depth; + const GLuint compare_coord = (tObj->Target == GL_TEXTURE_2D_ARRAY_EXT) + ? 3 : 2; + GLfloat ambient; + GLenum function; + GLfloat result; + + ASSERT(img->_BaseFormat == GL_DEPTH_COMPONENT || + img->_BaseFormat == GL_DEPTH_STENCIL_EXT); + + ASSERT(tObj->Target == GL_TEXTURE_1D || + tObj->Target == GL_TEXTURE_2D || + tObj->Target == GL_TEXTURE_RECTANGLE_NV || + tObj->Target == GL_TEXTURE_1D_ARRAY_EXT || + tObj->Target == GL_TEXTURE_2D_ARRAY_EXT); + + ambient = tObj->Sampler.CompareFailValue; + + /* XXXX if tObj->Sampler.MinFilter != tObj->Sampler.MagFilter, we're ignoring lambda */ + + function = (tObj->Sampler.CompareMode == GL_COMPARE_R_TO_TEXTURE_ARB) ? + tObj->Sampler.CompareFunc : GL_NONE; + + if (tObj->Sampler.MagFilter == GL_NEAREST) { + GLuint i; + for (i = 0; i < n; i++) { + GLfloat depthSample, depthRef; + GLint col, row, slice; + + nearest_texcoord(tObj, level, texcoords[i], &col, &row, &slice); + + if (col >= 0 && row >= 0 && col < width && row < height && + slice >= 0 && slice < depth) { + img->FetchTexelf(img, col, row, slice, &depthSample); + } + else { + depthSample = tObj->Sampler.BorderColor.f[0]; + } + + depthRef = CLAMP(texcoords[i][compare_coord], 0.0F, 1.0F); + + result = shadow_compare(function, depthRef, depthSample, ambient); + + switch (tObj->Sampler.DepthMode) { + case GL_LUMINANCE: + ASSIGN_4V(texel[i], result, result, result, 1.0F); + break; + case GL_INTENSITY: + ASSIGN_4V(texel[i], result, result, result, result); + break; + case GL_ALPHA: + ASSIGN_4V(texel[i], 0.0F, 0.0F, 0.0F, result); + break; + case GL_RED: + ASSIGN_4V(texel[i], result, 0.0F, 0.0F, 1.0F); + break; + default: + _mesa_problem(ctx, "Bad depth texture mode"); + } + } + } + else { + GLuint i; + ASSERT(tObj->Sampler.MagFilter == GL_LINEAR); + for (i = 0; i < n; i++) { + GLfloat depth00, depth01, depth10, depth11, depthRef; + GLint i0, i1, j0, j1; + GLint slice; + GLfloat wi, wj; + GLuint useBorderTexel; + + linear_texcoord(tObj, level, texcoords[i], &i0, &i1, &j0, &j1, &slice, + &wi, &wj); + + useBorderTexel = 0; + if (img->Border) { + i0 += img->Border; + i1 += img->Border; + if (tObj->Target != GL_TEXTURE_1D_ARRAY_EXT) { + j0 += img->Border; + j1 += img->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; + } + + if (slice < 0 || slice >= (GLint) depth) { + depth00 = tObj->Sampler.BorderColor.f[0]; + depth01 = tObj->Sampler.BorderColor.f[0]; + depth10 = tObj->Sampler.BorderColor.f[0]; + depth11 = tObj->Sampler.BorderColor.f[0]; + } + else { + /* get four depth samples from the texture */ + if (useBorderTexel & (I0BIT | J0BIT)) { + depth00 = tObj->Sampler.BorderColor.f[0]; + } + else { + img->FetchTexelf(img, i0, j0, slice, &depth00); + } + if (useBorderTexel & (I1BIT | J0BIT)) { + depth10 = tObj->Sampler.BorderColor.f[0]; + } + else { + img->FetchTexelf(img, i1, j0, slice, &depth10); + } + + if (tObj->Target != GL_TEXTURE_1D_ARRAY_EXT) { + if (useBorderTexel & (I0BIT | J1BIT)) { + depth01 = tObj->Sampler.BorderColor.f[0]; + } + else { + img->FetchTexelf(img, i0, j1, slice, &depth01); + } + if (useBorderTexel & (I1BIT | J1BIT)) { + depth11 = tObj->Sampler.BorderColor.f[0]; + } + else { + img->FetchTexelf(img, i1, j1, slice, &depth11); + } + } + else { + depth01 = depth00; + depth11 = depth10; + } + } + + depthRef = CLAMP(texcoords[i][compare_coord], 0.0F, 1.0F); + + result = shadow_compare4(function, depthRef, + depth00, depth01, depth10, depth11, + ambient, wi, wj); + + switch (tObj->Sampler.DepthMode) { + case GL_LUMINANCE: + ASSIGN_4V(texel[i], result, result, result, 1.0F); + break; + case GL_INTENSITY: + ASSIGN_4V(texel[i], result, result, result, result); + break; + case GL_ALPHA: + ASSIGN_4V(texel[i], 0.0F, 0.0F, 0.0F, result); + break; + default: + _mesa_problem(ctx, "Bad depth texture mode"); + } + + } /* for */ + } /* if filter */ +} + + +/** + * We use this function when a texture object is in an "incomplete" state. + * When a fragment program attempts to sample an incomplete texture we + * return black (see issue 23 in GL_ARB_fragment_program spec). + * Note: fragment programs don't observe the texture enable/disable flags. + */ +static void +null_sample_func( struct gl_context *ctx, + const struct gl_texture_object *tObj, GLuint n, + const GLfloat texcoords[][4], const GLfloat lambda[], + GLfloat rgba[][4]) +{ + GLuint i; + (void) ctx; + (void) tObj; + (void) texcoords; + (void) lambda; + for (i = 0; i < n; i++) { + rgba[i][RCOMP] = 0; + rgba[i][GCOMP] = 0; + rgba[i][BCOMP] = 0; + rgba[i][ACOMP] = 1.0; + } +} + + +/** + * Choose the texture sampling function for the given texture object. + */ +texture_sample_func +_swrast_choose_texture_sample_func( struct gl_context *ctx, + const struct gl_texture_object *t ) +{ + if (!t || !t->_Complete) { + return &null_sample_func; + } + else { + const GLboolean needLambda = + (GLboolean) (t->Sampler.MinFilter != t->Sampler.MagFilter); + const GLenum format = t->Image[0][t->BaseLevel]->_BaseFormat; + + switch (t->Target) { + case GL_TEXTURE_1D: + if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL_EXT) { + return &sample_depth_texture; + } + else if (needLambda) { + return &sample_lambda_1d; + } + else if (t->Sampler.MinFilter == GL_LINEAR) { + return &sample_linear_1d; + } + else { + ASSERT(t->Sampler.MinFilter == GL_NEAREST); + return &sample_nearest_1d; + } + case GL_TEXTURE_2D: + if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL_EXT) { + return &sample_depth_texture; + } + else if (needLambda) { + /* Anisotropic filtering extension. Activated only if mipmaps are used */ + if (t->Sampler.MaxAnisotropy > 1.0 && + t->Sampler.MinFilter == GL_LINEAR_MIPMAP_LINEAR) { + return &sample_lambda_2d_aniso; + } + return &sample_lambda_2d; + } + else if (t->Sampler.MinFilter == GL_LINEAR) { + return &sample_linear_2d; + } + else { + /* check for a few optimized cases */ + const struct gl_texture_image *img = t->Image[0][t->BaseLevel]; + ASSERT(t->Sampler.MinFilter == GL_NEAREST); + if (t->Sampler.WrapS == GL_REPEAT && + t->Sampler.WrapT == GL_REPEAT && + img->_IsPowerOfTwo && + img->Border == 0 && + img->TexFormat == MESA_FORMAT_RGB888) { + return &opt_sample_rgb_2d; + } + else if (t->Sampler.WrapS == GL_REPEAT && + t->Sampler.WrapT == GL_REPEAT && + img->_IsPowerOfTwo && + img->Border == 0 && + img->TexFormat == MESA_FORMAT_RGBA8888) { + return &opt_sample_rgba_2d; + } + else { + return &sample_nearest_2d; + } + } + case GL_TEXTURE_3D: + if (needLambda) { + return &sample_lambda_3d; + } + else if (t->Sampler.MinFilter == GL_LINEAR) { + return &sample_linear_3d; + } + else { + ASSERT(t->Sampler.MinFilter == GL_NEAREST); + return &sample_nearest_3d; + } + case GL_TEXTURE_CUBE_MAP: + if (needLambda) { + return &sample_lambda_cube; + } + else if (t->Sampler.MinFilter == GL_LINEAR) { + return &sample_linear_cube; + } + else { + ASSERT(t->Sampler.MinFilter == GL_NEAREST); + return &sample_nearest_cube; + } + case GL_TEXTURE_RECTANGLE_NV: + if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL_EXT) { + return &sample_depth_texture; + } + else if (needLambda) { + return &sample_lambda_rect; + } + else if (t->Sampler.MinFilter == GL_LINEAR) { + return &sample_linear_rect; + } + else { + ASSERT(t->Sampler.MinFilter == GL_NEAREST); + return &sample_nearest_rect; + } + case GL_TEXTURE_1D_ARRAY_EXT: + if (needLambda) { + return &sample_lambda_1d_array; + } + else if (t->Sampler.MinFilter == GL_LINEAR) { + return &sample_linear_1d_array; + } + else { + ASSERT(t->Sampler.MinFilter == GL_NEAREST); + return &sample_nearest_1d_array; + } + case GL_TEXTURE_2D_ARRAY_EXT: + if (needLambda) { + return &sample_lambda_2d_array; + } + else if (t->Sampler.MinFilter == GL_LINEAR) { + return &sample_linear_2d_array; + } + else { + ASSERT(t->Sampler.MinFilter == GL_NEAREST); + return &sample_nearest_2d_array; + } + default: + _mesa_problem(ctx, + "invalid target in _swrast_choose_texture_sample_func"); + return &null_sample_func; + } + } +} |