blob: 101439434193462db0b5f26dc14f9abd04d5829d [file] [log] [blame]
/*
* These values are used in the fixed-point arithmetic used
* for linear filtering.
*/
#define WEIGHT_SCALE 65536.0F
#define WEIGHT_SHIFT 16
/*
* Compute the remainder of a divided by b, but be careful with
* negative values so that GL_REPEAT mode works right.
*/
static INLINE GLint
repeat_remainder(GLint a, GLint b)
{
if (a >= 0)
return a % b;
else
return (a + 1) % b + b - 1;
}
/*
* 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:
* U = texcoord in [0, width]
* I0, I1 = two nearest texel indexes
*/
#define COMPUTE_LINEAR_TEXEL_LOCATIONS(wrapMode, S, U, SIZE, I0, I1) \
{ \
if (wrapMode == GL_REPEAT) { \
U = S * SIZE - 0.5F; \
if (tObj->_IsPowerOfTwo) { \
I0 = IFLOOR(U) & (SIZE - 1); \
I1 = (I0 + 1) & (SIZE - 1); \
} \
else { \
I0 = repeat_remainder(IFLOOR(U), SIZE); \
I1 = repeat_remainder(I0 + 1, SIZE); \
} \
} \
else if (wrapMode == 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; \
} \
else if (wrapMode == 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; \
} \
else if (wrapMode == GL_MIRRORED_REPEAT) { \
const GLint flr = IFLOOR(S); \
if (flr & 1) \
U = 1.0F - (S - (GLfloat) flr); /* flr is odd */ \
else \
U = S - (GLfloat) flr; /* flr is even */ \
U = (U * SIZE) - 0.5F; \
I0 = IFLOOR(U); \
I1 = I0 + 1; \
if (I0 < 0) \
I0 = 0; \
if (I1 >= (GLint) SIZE) \
I1 = SIZE - 1; \
} \
else if (wrapMode == GL_MIRROR_CLAMP_EXT) { \
U = (GLfloat) fabs(S); \
if (U >= 1.0F) \
U = (GLfloat) SIZE; \
else \
U *= SIZE; \
U -= 0.5F; \
I0 = IFLOOR(U); \
I1 = I0 + 1; \
} \
else if (wrapMode == GL_MIRROR_CLAMP_TO_EDGE_EXT) { \
U = (GLfloat) fabs(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; \
} \
else if (wrapMode == GL_MIRROR_CLAMP_TO_BORDER_EXT) { \
const GLfloat min = -1.0F / (2.0F * SIZE); \
const GLfloat max = 1.0F - min; \
U = (GLfloat) fabs(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; \
} \
else { \
ASSERT(wrapMode == 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; \
} \
}
/*
* Used to compute texel location for nearest sampling.
*/
#define COMPUTE_NEAREST_TEXEL_LOCATION(wrapMode, S, SIZE, I) \
{ \
if (wrapMode == GL_REPEAT) { \
/* s limited to [0,1) */ \
/* i limited to [0,size-1] */ \
I = IFLOOR(S * SIZE); \
if (tObj->_IsPowerOfTwo) \
I &= (SIZE - 1); \
else \
I = repeat_remainder(I, SIZE); \
} \
else if (wrapMode == 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); \
} \
else if (wrapMode == 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); \
} \
else if (wrapMode == 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); /* flr is odd */ \
else \
u = S - (GLfloat) flr; /* flr is even */ \
if (u < min) \
I = 0; \
else if (u > max) \
I = SIZE - 1; \
else \
I = IFLOOR(u * SIZE); \
} \
else if (wrapMode == GL_MIRROR_CLAMP_EXT) { \
/* s limited to [0,1] */ \
/* i limited to [0,size-1] */ \
const GLfloat u = (GLfloat) fabs(S); \
if (u <= 0.0F) \
I = 0; \
else if (u >= 1.0F) \
I = SIZE - 1; \
else \
I = IFLOOR(u * SIZE); \
} \
else if (wrapMode == 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 = (GLfloat) fabs(S); \
if (u < min) \
I = 0; \
else if (u > max) \
I = SIZE - 1; \
else \
I = IFLOOR(u * SIZE); \
} \
else if (wrapMode == 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 = (GLfloat) fabs(S); \
if (u < min) \
I = -1; \
else if (u > max) \
I = SIZE; \
else \
I = IFLOOR(u * SIZE); \
} \
else { \
ASSERT(wrapMode == 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); \
} \
}
/* Power of two image sizes only */
#define COMPUTE_LINEAR_REPEAT_TEXEL_LOCATION(S, U, SIZE, I0, I1) \
{ \
U = S * SIZE - 0.5F; \
I0 = IFLOOR(U) & (SIZE - 1); \
I1 = (I0 + 1) & (SIZE - 1); \
}
/*
* Compute linear mipmap levels for given lambda.
*/
#define COMPUTE_LINEAR_MIPMAP_LEVEL(tObj, lambda, level) \
{ \
if (lambda < 0.0F) \
level = tObj->BaseLevel; \
else if (lambda > tObj->_MaxLambda) \
level = (GLint) (tObj->BaseLevel + tObj->_MaxLambda); \
else \
level = (GLint) (tObj->BaseLevel + lambda); \
}
/*
* Compute nearest mipmap level for given lambda.
*/
#define COMPUTE_NEAREST_MIPMAP_LEVEL(tObj, lambda, level) \
{ \
GLfloat l; \
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; \
}
/*
* Note, the FRAC macro has to work perfectly. Otherwise you'll sometimes
* see 1-pixel bands of improperly weighted linear-sampled texels. 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))
/*
* Bitflags for texture border color sampling.
*/
#define I0BIT 1
#define I1BIT 2
#define J0BIT 4
#define J1BIT 8
#define K0BIT 16
#define K1BIT 32
#if 000
/*
* Get texture palette entry.
*/
static void
palette_sample(const GLcontext *ctx,
const struct gl_texture_object *tObj,
GLint index, GLchan rgba[4] )
{
const GLchan *palette;
GLenum format;
if (ctx->Texture.SharedPalette) {
ASSERT(ctx->Texture.Palette.Type != GL_FLOAT);
palette = (const GLchan *) ctx->Texture.Palette.Table;
format = ctx->Texture.Palette.Format;
}
else {
ASSERT(tObj->Palette.Type != GL_FLOAT);
palette = (const GLchan *) tObj->Palette.Table;
format = tObj->Palette.Format;
}
switch (format) {
case GL_ALPHA:
rgba[ACOMP] = palette[index];
return;
case GL_LUMINANCE:
case GL_INTENSITY:
rgba[RCOMP] = palette[index];
return;
case GL_LUMINANCE_ALPHA:
rgba[RCOMP] = palette[(index << 1) + 0];
rgba[ACOMP] = palette[(index << 1) + 1];
return;
case GL_RGB:
rgba[RCOMP] = palette[index * 3 + 0];
rgba[GCOMP] = palette[index * 3 + 1];
rgba[BCOMP] = palette[index * 3 + 2];
return;
case GL_RGBA:
rgba[RCOMP] = palette[(index << 2) + 0];
rgba[GCOMP] = palette[(index << 2) + 1];
rgba[BCOMP] = palette[(index << 2) + 2];
rgba[ACOMP] = palette[(index << 2) + 3];
return;
default:
_mesa_problem(ctx, "Bad palette format in palette_sample");
}
}
#endif
/**********************************************************************/
/* 1-D Texture Sampling Functions */
/**********************************************************************/
/*
* Return the texture sample for coordinate (s) using GL_NEAREST filter.
*/
static void
sample_1d_nearest(GLcontext *ctx,
const struct gl_texture_object *tObj,
const struct gl_texture_image *img,
const GLfloat texcoord[4], TYPE rgba[4])
{
const GLint width = img->Width2; /* without border */
GLint i;
COMPUTE_NEAREST_TEXEL_LOCATION(tObj->WrapS, texcoord[0], width, i);
/* 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 */
#if TYPE_ENUM == GL_FLOAT
COPY_4V(rgba, tObj->BorderColor);
#else
COPY_CHAN4(rgba, tObj->_BorderChan);
#endif
}
else {
#if TYPE_ENUM == GL_FLOAT
img->FetchTexelf(img, i, 0, 0, rgba);
#else
img->FetchTexelc(img, i, 0, 0, rgba);
#endif
if (img->Format == GL_COLOR_INDEX) {
palette_sample(ctx, tObj, rgba[0], rgba);
}
}
}
/*
* Return the texture sample for coordinate (s) using GL_LINEAR filter.
*/
static void
sample_1d_linear(GLcontext *ctx,
const struct gl_texture_object *tObj,
const struct gl_texture_image *img,
const GLfloat texcoord[4], GLchan rgba[4])
{
const GLint width = img->Width2;
GLint i0, i1;
GLfloat u;
GLuint useBorderColor;
COMPUTE_LINEAR_TEXEL_LOCATIONS(tObj->WrapS, texcoord[0], u, width, i0, i1);
useBorderColor = 0;
if (img->Border) {
i0 += img->Border;
i1 += img->Border;
}
else {
if (i0 < 0 || i0 >= width) useBorderColor |= I0BIT;
if (i1 < 0 || i1 >= width) useBorderColor |= I1BIT;
}
{
const GLfloat a = FRAC(u);
#if CHAN_TYPE == GL_FLOAT || CHAN_TYPE == GL_UNSIGNED_SHORT
const GLfloat w0 = (1.0F-a);
const GLfloat w1 = a ;
#else /* CHAN_BITS == 8 */
/* compute sample weights in fixed point in [0,WEIGHT_SCALE] */
const GLint w0 = IROUND_POS((1.0F - a) * WEIGHT_SCALE);
const GLint w1 = IROUND_POS( a * WEIGHT_SCALE);
#endif
GLchan t0[4], t1[4]; /* texels */
if (useBorderColor & I0BIT) {
COPY_CHAN4(t0, tObj->_BorderChan);
}
else {
img->FetchTexelc(img, i0, 0, 0, t0);
if (img->Format == GL_COLOR_INDEX) {
palette_sample(ctx, tObj, t0[0], t0);
}
}
if (useBorderColor & I1BIT) {
COPY_CHAN4(t1, tObj->_BorderChan);
}
else {
img->FetchTexelc(img, i1, 0, 0, t1);
if (img->Format == GL_COLOR_INDEX) {
palette_sample(ctx, tObj, t1[0], t1);
}
}
#if CHAN_TYPE == GL_FLOAT
rgba[0] = w0 * t0[0] + w1 * t1[0];
rgba[1] = w0 * t0[1] + w1 * t1[1];
rgba[2] = w0 * t0[2] + w1 * t1[2];
rgba[3] = w0 * t0[3] + w1 * t1[3];
#elif CHAN_TYPE == GL_UNSIGNED_SHORT
rgba[0] = (GLchan) (w0 * t0[0] + w1 * t1[0] + 0.5);
rgba[1] = (GLchan) (w0 * t0[1] + w1 * t1[1] + 0.5);
rgba[2] = (GLchan) (w0 * t0[2] + w1 * t1[2] + 0.5);
rgba[3] = (GLchan) (w0 * t0[3] + w1 * t1[3] + 0.5);
#else /* CHAN_BITS == 8 */
rgba[0] = (GLchan) ((w0 * t0[0] + w1 * t1[0]) >> WEIGHT_SHIFT);
rgba[1] = (GLchan) ((w0 * t0[1] + w1 * t1[1]) >> WEIGHT_SHIFT);
rgba[2] = (GLchan) ((w0 * t0[2] + w1 * t1[2]) >> WEIGHT_SHIFT);
rgba[3] = (GLchan) ((w0 * t0[3] + w1 * t1[3]) >> WEIGHT_SHIFT);
#endif
}
}
static void
sample_1d_nearest_mipmap_nearest(GLcontext *ctx,
const struct gl_texture_object *tObj,
GLuint n, const GLfloat texcoord[][4],
const GLfloat lambda[], GLchan rgba[][4])
{
GLuint i;
ASSERT(lambda != NULL);
for (i = 0; i < n; i++) {
GLint level;
COMPUTE_NEAREST_MIPMAP_LEVEL(tObj, lambda[i], level);
sample_1d_nearest(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]);
}
}
static void
sample_1d_linear_mipmap_nearest(GLcontext *ctx,
const struct gl_texture_object *tObj,
GLuint n, const GLfloat texcoord[][4],
const GLfloat lambda[], GLchan rgba[][4])
{
GLuint i;
ASSERT(lambda != NULL);
for (i = 0; i < n; i++) {
GLint level;
COMPUTE_NEAREST_MIPMAP_LEVEL(tObj, lambda[i], level);
sample_1d_linear(ctx, tObj, tObj->Image[0][level], texcoord[i], rgba[i]);
}
}
/*
* This is really just needed in order to prevent warnings with some compilers.
*/
#if CHAN_TYPE == GL_FLOAT
#define CHAN_CAST
#else
#define CHAN_CAST (GLchan) (GLint)
#endif
static void
sample_1d_nearest_mipmap_linear(GLcontext *ctx,
const struct gl_texture_object *tObj,
GLuint n, const GLfloat texcoord[][4],
const GLfloat lambda[], GLchan rgba[][4])
{
GLuint i;
ASSERT(lambda != NULL);
for (i = 0; i < n; i++) {
GLint level;
COMPUTE_LINEAR_MIPMAP_LEVEL(tObj, lambda[i], level);
if (level >= tObj->_MaxLevel) {
sample_1d_nearest(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
texcoord[i], rgba[i]);
}
else {
GLchan 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);
rgba[i][RCOMP] = CHAN_CAST ((1.0F-f) * t0[RCOMP] + f * t1[RCOMP]);
rgba[i][GCOMP] = CHAN_CAST ((1.0F-f) * t0[GCOMP] + f * t1[GCOMP]);
rgba[i][BCOMP] = CHAN_CAST ((1.0F-f) * t0[BCOMP] + f * t1[BCOMP]);
rgba[i][ACOMP] = CHAN_CAST ((1.0F-f) * t0[ACOMP] + f * t1[ACOMP]);
}
}
}
static void
sample_1d_linear_mipmap_linear(GLcontext *ctx,
const struct gl_texture_object *tObj,
GLuint n, const GLfloat texcoord[][4],
const GLfloat lambda[], GLchan rgba[][4])
{
GLuint i;
ASSERT(lambda != NULL);
for (i = 0; i < n; i++) {
GLint level;
COMPUTE_LINEAR_MIPMAP_LEVEL(tObj, lambda[i], level);
if (level >= tObj->_MaxLevel) {
sample_1d_linear(ctx, tObj, tObj->Image[0][tObj->_MaxLevel],
texcoord[i], rgba[i]);
}
else {
GLchan 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);
rgba[i][RCOMP] = CHAN_CAST ((1.0F-f) * t0[RCOMP] + f * t1[RCOMP]);
rgba[i][GCOMP] = CHAN_CAST ((1.0F-f) * t0[GCOMP] + f * t1[GCOMP]);
rgba[i][BCOMP] = CHAN_CAST ((1.0F-f) * t0[BCOMP] + f * t1[BCOMP]);
rgba[i][ACOMP] = CHAN_CAST ((1.0F-f) * t0[ACOMP] + f * t1[ACOMP]);
}
}
}
static void
sample_nearest_1d( GLcontext *ctx, GLuint texUnit,
const struct gl_texture_object *tObj, GLuint n,
const GLfloat texcoords[][4], const GLfloat lambda[],
GLchan 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]);
}
}
static void
sample_linear_1d( GLcontext *ctx, GLuint texUnit,
const struct gl_texture_object *tObj, GLuint n,
const GLfloat texcoords[][4], const GLfloat lambda[],
GLchan 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]);
}
}
/*
* Given an (s) texture coordinate and lambda (level of detail) value,
* return a texture sample.
*
*/
static void
sample_lambda_1d( GLcontext *ctx, GLuint texUnit,
const struct gl_texture_object *tObj, GLuint n,
const GLfloat texcoords[][4],
const GLfloat lambda[], GLchan rgba[][4] )
{
GLuint minStart, minEnd; /* texels with minification */
GLuint magStart, magEnd; /* texels with magnification */
GLuint i;
ASSERT(lambda != NULL);
compute_min_mag_ranges(SWRAST_CONTEXT(ctx)->_MinMagThresh[texUnit],
n, lambda, &minStart, &minEnd, &magStart, &magEnd);
if (minStart < minEnd) {
/* do the minified texels */
const GLuint m = minEnd - minStart;
switch (tObj->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->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;
}
}
}