/*
 * Mesa 3-D graphics library
 * Version:  4.0
 *
 * Copyright (C) 1999-2001  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.
 */

/* Authors:
 *    David Bucciarelli
 *    Brian Paul
 *    Daryll Strauss
 *    Keith Whitwell
 *    Daniel Borca
 *    Hiroshi Morii
 */


#ifdef HAVE_CONFIG_H
#include "conf.h"
#endif

#if defined(FX)

#include "fxdrv.h"
#include "main/enums.h"
#include "main/formats.h"
#include "main/image.h"
#include "main/teximage.h"
#include "main/texstore.h"
#include "main/texformat.h"
#include "main/texcompress.h"
#include "main/texobj.h"
#include "main/texstore.h"


/* no borders! can't halve 1x1! (stride > width * comp) not allowed */
static void
_mesa_halve2x2_teximage2d ( GLcontext *ctx,
			    struct gl_texture_image *texImage,
			    GLuint bytesPerPixel,
			    GLint srcWidth, GLint srcHeight,
			    const GLvoid *srcImage, GLvoid *dstImage )
{
   GLint i, j, k;
   GLint dstWidth = srcWidth / 2;
   GLint dstHeight = srcHeight / 2;
   GLint srcRowStride = srcWidth * bytesPerPixel;
   GLubyte *src = (GLubyte *)srcImage;
   GLubyte *dst = dstImage;

   GLuint bpt = 0;
   GLubyte *_s = NULL;
   GLubyte *_d = NULL;
   GLenum _t = 0;

   if (texImage->TexFormat->MesaFormat == MESA_FORMAT_RGB565) {
      _t = GL_UNSIGNED_SHORT_5_6_5_REV;
      bpt = bytesPerPixel;
   } else if (texImage->TexFormat->MesaFormat == MESA_FORMAT_ARGB4444) {
      _t = GL_UNSIGNED_SHORT_4_4_4_4_REV;
      bpt = bytesPerPixel;
   } else if (texImage->TexFormat->MesaFormat == MESA_FORMAT_ARGB1555) {
      _t = GL_UNSIGNED_SHORT_1_5_5_5_REV;
      bpt = bytesPerPixel;
   }
   if (bpt) {
      bytesPerPixel = 4;
      srcRowStride = srcWidth * bytesPerPixel;
      if (dstWidth == 0) {
         dstWidth = 1;
      }
      if (dstHeight == 0) {
         dstHeight = 1;
      }
      _s = src = MALLOC(srcRowStride * srcHeight);
      _d = dst = MALLOC(dstWidth * bytesPerPixel * dstHeight);
      _mesa_texstore(ctx, 2, GL_RGBA,
                     &_mesa_texformat_rgba8888_rev, src,
                     0, 0, 0, /* dstX/Y/Zoffset */
                     srcRowStride, /* dstRowStride */
                     0, /* dstImageStride */
                     srcWidth, srcHeight, 1,
                     texImage->_BaseFormat, _t,
                     srcImage, &ctx->DefaultPacking);
   }

   if (srcHeight == 1) {
      for (i = 0; i < dstWidth; i++) {
         for (k = 0; k < bytesPerPixel; k++) {
            dst[0] = (src[0] + src[bytesPerPixel] + 1) / 2;
            src++;
            dst++;
         }
         src += bytesPerPixel;
      }
   } else if (srcWidth == 1) {
      for (j = 0; j < dstHeight; j++) {
         for (k = 0; k < bytesPerPixel; k++) {
            dst[0] = (src[0] + src[srcRowStride] + 1) / 2;
            src++;
            dst++;
         }
         src += srcRowStride;
      }
   } else {
      for (j = 0; j < dstHeight; j++) {
         for (i = 0; i < dstWidth; i++) {
            for (k = 0; k < bytesPerPixel; k++) {
               dst[0] = (src[0] +
                         src[bytesPerPixel] +
                         src[srcRowStride] +
                         src[srcRowStride + bytesPerPixel] + 2) / 4;
               src++;
               dst++;
            }
            src += bytesPerPixel;
         }
         src += srcRowStride;
      }
   }

   if (bpt) {
      src = _s;
      dst = _d;
      _mesa_texstore(ctx, 2, texImage->_BaseFormat,
                     texImage->TexFormat, dstImage,
                     0, 0, 0, /* dstX/Y/Zoffset */
                     dstWidth * bpt,
                     0, /* dstImageStride */
                     dstWidth, dstHeight, 1,
                     GL_BGRA, CHAN_TYPE, dst, &ctx->DefaultPacking);
      FREE(dst);
      FREE(src);
   }
}


void
fxPrintTextureData(tfxTexInfo * ti)
{
   fprintf(stderr, "Texture Data:\n");
   if (ti->tObj) {
      fprintf(stderr, "\tName: %d\n", ti->tObj->Name);
      fprintf(stderr, "\tBaseLevel: %d\n", ti->tObj->BaseLevel);
      fprintf(stderr, "\tSize: %d x %d\n",
	      ti->tObj->Image[0][ti->tObj->BaseLevel]->Width,
	      ti->tObj->Image[0][ti->tObj->BaseLevel]->Height);
   }
   else
      fprintf(stderr, "\tName: UNNAMED\n");
   fprintf(stderr, "\tLast used: %d\n", ti->lastTimeUsed);
   fprintf(stderr, "\tTMU: %ld\n", ti->whichTMU);
   fprintf(stderr, "\t%s\n", (ti->isInTM) ? "In TMU" : "Not in TMU");
   if (ti->tm[0])
      fprintf(stderr, "\tMem0: %x-%x\n", (unsigned) ti->tm[0]->startAddr,
	      (unsigned) ti->tm[0]->endAddr);
   if (ti->tm[1])
      fprintf(stderr, "\tMem1: %x-%x\n", (unsigned) ti->tm[1]->startAddr,
	      (unsigned) ti->tm[1]->endAddr);
   fprintf(stderr, "\tMipmaps: %d-%d\n", ti->minLevel, ti->maxLevel);
   fprintf(stderr, "\tFilters: min %d max %d\n",
	   (int) ti->minFilt, (int) ti->maxFilt);
   fprintf(stderr, "\tClamps: s %d t %d\n", (int) ti->sClamp,
	   (int) ti->tClamp);
   fprintf(stderr, "\tScales: s %f t %f\n", ti->sScale, ti->tScale);
   fprintf(stderr, "\t%s\n",
	   (ti->fixedPalette) ? "Fixed palette" : "Non fixed palette");
   fprintf(stderr, "\t%s\n", (ti->validated) ? "Validated" : "Not validated");
}


/************************************************************************/
/*************************** Texture Mapping ****************************/
/************************************************************************/

static void
fxTexInvalidate(GLcontext * ctx, struct gl_texture_object *tObj)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxTexInfo *ti;

   ti = fxTMGetTexInfo(tObj);
   if (ti->isInTM)
      fxTMMoveOutTM(fxMesa, tObj);	/* TO DO: SLOW but easy to write */

   ti->validated = GL_FALSE;
   fxMesa->new_state |= FX_NEW_TEXTURING;
}

static tfxTexInfo *
fxAllocTexObjData(fxMesaContext fxMesa)
{
   tfxTexInfo *ti;

   if (!(ti = CALLOC(sizeof(tfxTexInfo)))) {
      fprintf(stderr, "fxAllocTexObjData: ERROR: out of memory !\n");
      fxCloseHardware();
      exit(-1);
   }

   ti->validated = GL_FALSE;
   ti->isInTM = GL_FALSE;

   ti->whichTMU = FX_TMU_NONE;

   ti->tm[FX_TMU0] = NULL;
   ti->tm[FX_TMU1] = NULL;

   ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
   ti->maxFilt = GR_TEXTUREFILTER_BILINEAR;

   ti->sClamp = GR_TEXTURECLAMP_WRAP;
   ti->tClamp = GR_TEXTURECLAMP_WRAP;

   ti->mmMode = GR_MIPMAP_NEAREST;
   ti->LODblend = FXFALSE;

   return ti;
}

void
fxDDTexBind(GLcontext * ctx, GLenum target, struct gl_texture_object *tObj)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxTexInfo *ti;

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxDDTexBind(%d, %x)\n", tObj->Name, (GLuint)tObj->DriverData);
   }

   if ((target != GL_TEXTURE_1D) && (target != GL_TEXTURE_2D))
      return;

   if (!tObj->DriverData) {
      tObj->DriverData = fxAllocTexObjData(fxMesa);
   }
   ti = fxTMGetTexInfo(tObj);

   fxMesa->texBindNumber++;
   ti->lastTimeUsed = fxMesa->texBindNumber;

   fxMesa->new_state |= FX_NEW_TEXTURING;
}

void
fxDDTexEnv(GLcontext * ctx, GLenum target, GLenum pname,
	   const GLfloat * param)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      if (param)
	 fprintf(stderr, "fxDDTexEnv(%x, %x)\n", pname, (GLint) (*param));
      else
	 fprintf(stderr, "fxDDTexEnv(%x)\n", pname);
   }

   /* apply any lod biasing right now */
   if (pname == GL_TEXTURE_LOD_BIAS_EXT) {
      GLfloat bias = *param;
      CLAMP_SELF(bias, -ctx->Const.MaxTextureLodBias,
                        ctx->Const.MaxTextureLodBias - 0.25);

      grTexLodBiasValue(GR_TMU0, bias);

      if (fxMesa->haveTwoTMUs) {
	 grTexLodBiasValue(GR_TMU1, bias);
      }

   }

   fxMesa->new_state |= FX_NEW_TEXTURING;
}

void
fxDDTexParam(GLcontext * ctx, GLenum target, struct gl_texture_object *tObj,
	     GLenum pname, const GLfloat * params)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   GLenum param = (GLenum) (GLint) params[0];
   tfxTexInfo *ti;

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxDDTexParam(%d, %x, %s, %s)\n",
                      tObj->Name, (GLuint) tObj->DriverData,
                      _mesa_lookup_enum_by_nr(pname),
                      _mesa_lookup_enum_by_nr(param));
   }

   if ((target != GL_TEXTURE_1D) && (target != GL_TEXTURE_2D))
      return;

   if (!tObj->DriverData)
      tObj->DriverData = fxAllocTexObjData(fxMesa);
   ti = fxTMGetTexInfo(tObj);

   switch (pname) {
   case GL_TEXTURE_MIN_FILTER:
      switch (param) {
      case GL_NEAREST:
	 ti->mmMode = GR_MIPMAP_DISABLE;
	 ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
	 ti->LODblend = FXFALSE;
	 break;
      case GL_LINEAR:
	 ti->mmMode = GR_MIPMAP_DISABLE;
	 ti->minFilt = GR_TEXTUREFILTER_BILINEAR;
	 ti->LODblend = FXFALSE;
	 break;
      case GL_NEAREST_MIPMAP_LINEAR:
         /* [dBorca]
          * currently Napalm can't do single-pass trilinear,
          * because the way its combiners are set. So we fall back
          * to GL_NEAREST_MIPMAP_NEAREST. We'll let true trilinear
          * enabled for V2, V3.
          */
         if (!fxMesa->HaveCmbExt) {
	    if (fxMesa->haveTwoTMUs) {
	       ti->mmMode = GR_MIPMAP_NEAREST;
	       ti->LODblend = FXTRUE;
            } else {
	       ti->mmMode = GR_MIPMAP_NEAREST_DITHER;
	       ti->LODblend = FXFALSE;
            }
	    ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
	    break;
         }
      case GL_NEAREST_MIPMAP_NEAREST:
	 ti->mmMode = GR_MIPMAP_NEAREST;
	 ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
	 ti->LODblend = FXFALSE;
	 break;
      case GL_LINEAR_MIPMAP_LINEAR:
         /* [dBorca]
          * currently Napalm can't do single-pass trilinear,
          * because the way its combiners are set. So we fall back
          * to GL_LINEAR_MIPMAP_NEAREST. We'll let true trilinear
          * enabled for V2, V3.
          */
         if (!fxMesa->HaveCmbExt) {
            if (fxMesa->haveTwoTMUs) {
               ti->mmMode = GR_MIPMAP_NEAREST;
               ti->LODblend = FXTRUE;
            } else {
               ti->mmMode = GR_MIPMAP_NEAREST_DITHER;
               ti->LODblend = FXFALSE;
            }
            ti->minFilt = GR_TEXTUREFILTER_BILINEAR;
            break;
         }
      case GL_LINEAR_MIPMAP_NEAREST:
	 ti->mmMode = GR_MIPMAP_NEAREST;
	 ti->minFilt = GR_TEXTUREFILTER_BILINEAR;
	 ti->LODblend = FXFALSE;
	 break;
      default:
	 break;
      }
      fxTexInvalidate(ctx, tObj);
      break;

   case GL_TEXTURE_MAG_FILTER:
      switch (param) {
      case GL_NEAREST:
	 ti->maxFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
	 break;
      case GL_LINEAR:
	 ti->maxFilt = GR_TEXTUREFILTER_BILINEAR;
	 break;
      default:
	 break;
      }
      fxMesa->new_state |= FX_NEW_TEXTURING;
      break;

   case GL_TEXTURE_WRAP_S:
      switch (param) {
      case GL_MIRRORED_REPEAT:
         ti->sClamp = GR_TEXTURECLAMP_MIRROR_EXT;
         break;
      case GL_CLAMP_TO_BORDER: /* no-no, but don't REPEAT, either */
      case GL_CLAMP_TO_EDGE: /* CLAMP discarding border */
      case GL_CLAMP:
	 ti->sClamp = GR_TEXTURECLAMP_CLAMP;
	 break;
      case GL_REPEAT:
	 ti->sClamp = GR_TEXTURECLAMP_WRAP;
	 break;
      default:
	 break;
      }
      fxMesa->new_state |= FX_NEW_TEXTURING;
      break;

   case GL_TEXTURE_WRAP_T:
      switch (param) {
      case GL_MIRRORED_REPEAT:
         ti->tClamp = GR_TEXTURECLAMP_MIRROR_EXT;
         break;
      case GL_CLAMP_TO_BORDER: /* no-no, but don't REPEAT, either */
      case GL_CLAMP_TO_EDGE: /* CLAMP discarding border */
      case GL_CLAMP:
	 ti->tClamp = GR_TEXTURECLAMP_CLAMP;
	 break;
      case GL_REPEAT:
	 ti->tClamp = GR_TEXTURECLAMP_WRAP;
	 break;
      default:
	 break;
      }
      fxMesa->new_state |= FX_NEW_TEXTURING;
      break;

   case GL_TEXTURE_BORDER_COLOR:
      /* TO DO */
      break;

   case GL_TEXTURE_MIN_LOD:
      /* TO DO */
      break;
   case GL_TEXTURE_MAX_LOD:
      /* TO DO */
      break;
   case GL_TEXTURE_BASE_LEVEL:
      fxTexInvalidate(ctx, tObj);
      break;
   case GL_TEXTURE_MAX_LEVEL:
      fxTexInvalidate(ctx, tObj);
      break;

   default:
      break;
   }
}

void
fxDDTexDel(GLcontext * ctx, struct gl_texture_object *tObj)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxTexInfo *ti = fxTMGetTexInfo(tObj);

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxDDTexDel(%d, %p)\n", tObj->Name, (void *) ti);
   }

   if (!ti)
      return;

   fxTMFreeTexture(fxMesa, tObj);

   FREE(ti);
   tObj->DriverData = NULL;

   /* Free mipmap images and the texture object itself */
   _mesa_delete_texture_object(ctx, tObj);
}


/**
 * Allocate a new texture object.
 * Called via ctx->Driver.NewTextureObject.
 * Note: this function will be called during context creation to
 * allocate the default texture objects.
 */
struct gl_texture_object *
fxDDNewTextureObject( GLcontext *ctx, GLuint name, GLenum target )
{
   struct gl_texture_object *obj;
   obj = _mesa_new_texture_object(ctx, name, target);
   return obj;
}


/*
 * Return true if texture is resident, false otherwise.
 */
GLboolean
fxDDIsTextureResident(GLcontext *ctx, struct gl_texture_object *tObj)
{
   tfxTexInfo *ti = fxTMGetTexInfo(tObj);
   return (ti && ti->isInTM);
}



/*
 * Convert a gl_color_table texture palette to Glide's format.
 */
static GrTexTable_t
convertPalette(const fxMesaContext fxMesa, FxU32 data[256], const struct gl_color_table *table)
{
   const GLubyte *tableUB = (const GLubyte *) table->Table;
   GLint width = table->Size;
   FxU32 r, g, b, a;
   GLint i;

   ASSERT(table->Type == GL_UNSIGNED_BYTE);

   switch (table->_BaseFormat) {
   case GL_INTENSITY:
      for (i = 0; i < width; i++) {
	 r = tableUB[i];
	 g = tableUB[i];
	 b = tableUB[i];
	 a = tableUB[i];
	 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
      }
      return fxMesa->HavePalExt ? GR_TEXTABLE_PALETTE_6666_EXT : GR_TEXTABLE_PALETTE;
   case GL_LUMINANCE:
      for (i = 0; i < width; i++) {
	 r = tableUB[i];
	 g = tableUB[i];
	 b = tableUB[i];
	 a = 255;
	 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
      }
      return GR_TEXTABLE_PALETTE;
   case GL_ALPHA:
      for (i = 0; i < width; i++) {
	 r = g = b = 255;
	 a = tableUB[i];
	 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
      }
      return fxMesa->HavePalExt ? GR_TEXTABLE_PALETTE_6666_EXT : GR_TEXTABLE_PALETTE;
   case GL_LUMINANCE_ALPHA:
      for (i = 0; i < width; i++) {
	 r = g = b = tableUB[i * 2 + 0];
	 a = tableUB[i * 2 + 1];
	 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
      }
      return fxMesa->HavePalExt ? GR_TEXTABLE_PALETTE_6666_EXT : GR_TEXTABLE_PALETTE;
   default:
   case GL_RGB:
      for (i = 0; i < width; i++) {
	 r = tableUB[i * 3 + 0];
	 g = tableUB[i * 3 + 1];
	 b = tableUB[i * 3 + 2];
	 a = 255;
	 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
      }
      return GR_TEXTABLE_PALETTE;
   case GL_RGBA:
      for (i = 0; i < width; i++) {
	 r = tableUB[i * 4 + 0];
	 g = tableUB[i * 4 + 1];
	 b = tableUB[i * 4 + 2];
	 a = tableUB[i * 4 + 3];
	 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
      }
      return fxMesa->HavePalExt ? GR_TEXTABLE_PALETTE_6666_EXT : GR_TEXTABLE_PALETTE;
   }
}


void
fxDDTexPalette(GLcontext * ctx, struct gl_texture_object *tObj)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);

   if (tObj) {
      /* per-texture palette */
      tfxTexInfo *ti;
      if (TDFX_DEBUG & VERBOSE_DRIVER) {
	 fprintf(stderr, "fxDDTexPalette(%d, %x)\n",
		 tObj->Name, (GLuint) tObj->DriverData);
      }
      /* This might be a proxy texture. */
      if (!tObj->Palette.Table)
         return;
      if (!tObj->DriverData)
         tObj->DriverData = fxAllocTexObjData(fxMesa);
      ti = fxTMGetTexInfo(tObj);
      ti->paltype = convertPalette(fxMesa, ti->palette.data, &tObj->Palette);
      fxTexInvalidate(ctx, tObj);
   }
   else {
      /* global texture palette */
      if (TDFX_DEBUG & VERBOSE_DRIVER) {
	 fprintf(stderr, "fxDDTexPalette(global)\n");
      }
      fxMesa->glbPalType = convertPalette(fxMesa, fxMesa->glbPalette.data, &ctx->Texture.Palette);
      fxMesa->new_state |= FX_NEW_TEXTURING;

      grTexDownloadTable(fxMesa->glbPalType, &(fxMesa->glbPalette));
   }
}


void
fxDDTexUseGlbPalette(GLcontext * ctx, GLboolean state)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxDDTexUseGlbPalette(%d)\n", state);
   }

   fxMesa->haveGlobalPaletteTexture = state;
   fxMesa->new_state |= FX_NEW_TEXTURING;
}


static int
logbase2(int n)
{
   GLint i = 1;
   GLint log2 = 0;

   if (n < 0) {
      return -1;
   }

   while (n > i) {
      i *= 2;
      log2++;
   }
   if (i != n) {
      return -1;
   }
   else {
      return log2;
   }
}


/* fxTexGetInfo
 * w, h     - source texture width and height
 * lodlevel - Glide lod level token for the larger texture dimension
 * ar       - Glide aspect ratio token
 * sscale   - S scale factor used during triangle setup
 * tscale   - T scale factor used during triangle setup
 * wscale   - OpenGL -> Glide image width scale factor
 * hscale   - OpenGL -> Glide image height scale factor
 */
int
fxTexGetInfo(int w, int h, GrLOD_t * lodlevel, GrAspectRatio_t * ar,
	     float *sscale, float *tscale,
	     int *wscale, int *hscale)
{
   int logw, logh, ws, hs;
   GrLOD_t l;
   GrAspectRatio_t aspectratio;
   float s, t;

   logw = logbase2(w);
   logh = logbase2(h);

   l = MAX2(logw, logh);
   aspectratio = logw - logh;
   ws = hs = 1;
   s = t = 256.0f;

   /* hardware only allows a maximum aspect ratio of 8x1, so handle
    * |aspectratio| > 3 by scaling the image and using an 8x1 aspect
    * ratio
    */
   switch (aspectratio) {
   case 0:
      break;
   case 1:
      t = 128.0f;
      break;
   case 2:
      t = 64.0f;
      break;
   case 3:
      t = 32.0f;
      break;
   case -1:
      s = 128.0f;
      break;
   case -2:
      s = 64.0f;
      break;
   case -3:
      s = 32.0f;
      break;
   default:
      if (aspectratio > 3) {
         t = 32.0f;
         hs = 1 << (aspectratio - 3);
         aspectratio = GR_ASPECT_LOG2_8x1;
      } else /*if (aspectratio < -3)*/ {
         s = 32.0f;
         ws = 1 << (-aspectratio - 3);
         aspectratio = GR_ASPECT_LOG2_1x8;
      }
   }

   if (lodlevel)
      (*lodlevel) = l;

   if (ar)
      (*ar) = aspectratio;

   if (sscale)
      (*sscale) = s;

   if (tscale)
      (*tscale) = t;

   if (wscale)
      (*wscale) = ws;

   if (hscale)
      (*hscale) = hs;


   return 1;
}

static GLboolean
fxIsTexSupported(GLenum target, GLint internalFormat,
		 const struct gl_texture_image *image)
{
   if ((target != GL_TEXTURE_1D) && (target != GL_TEXTURE_2D))
      return GL_FALSE;

#if 0
   if (!fxTexGetInfo(image->Width, image->Height, NULL, NULL, NULL, NULL, NULL, NULL))
       return GL_FALSE;
#endif

   if (image->Border > 0)
      return GL_FALSE;

   return GL_TRUE;
}


/**********************************************************************/
/**** NEW TEXTURE IMAGE FUNCTIONS                                  ****/
/**********************************************************************/
extern void
fxt1_decode_1 (const void *texture, int width,
               int i, int j, unsigned char *rgba);

/* Texel-fetch functions for software texturing and glGetTexImage().
 * We should have been able to use some "standard" fetch functions (which
 * may get defined in texutil.c) but we have to account for scaled texture
 * images on tdfx hardware (the 8:1 aspect ratio limit).
 * Hence, we need special functions here.
 */

static void
fetch_intensity8(const struct gl_texture_image *texImage,
		 GLint i, GLint j, GLint k, GLchan *rgba)
{
   const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
   const GLubyte *texel;

   i = i * mml->wScale;
   j = j * mml->hScale;

   texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
   rgba[RCOMP] = *texel;
   rgba[GCOMP] = *texel;
   rgba[BCOMP] = *texel;
   rgba[ACOMP] = *texel;
}


static void
fetch_luminance8(const struct gl_texture_image *texImage,
		 GLint i, GLint j, GLint k, GLchan *rgba)
{
   const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
   const GLubyte *texel;

   i = i * mml->wScale;
   j = j * mml->hScale;

   texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
   rgba[RCOMP] = *texel;
   rgba[GCOMP] = *texel;
   rgba[BCOMP] = *texel;
   rgba[ACOMP] = 255;
}


static void
fetch_alpha8(const struct gl_texture_image *texImage,
	     GLint i, GLint j, GLint k, GLchan *rgba)
{
   const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
   const GLubyte *texel;

   i = i * mml->wScale;
   j = j * mml->hScale;

   texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
   rgba[RCOMP] = 255;
   rgba[GCOMP] = 255;
   rgba[BCOMP] = 255;
   rgba[ACOMP] = *texel;
}


static void
fetch_index8(const struct gl_texture_image *texImage,
	     GLint i, GLint j, GLint k, GLchan *indexOut)
{
   const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
   const GLubyte *texel;

   i = i * mml->wScale;
   j = j * mml->hScale;

   texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
   *indexOut = *texel;
}


static void
fetch_luminance8_alpha8(const struct gl_texture_image *texImage,
			GLint i, GLint j, GLint k, GLchan *rgba)
{
   const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
   const GLubyte *texel;

   i = i * mml->wScale;
   j = j * mml->hScale;

   texel = ((GLubyte *) texImage->Data) + (j * mml->width + i) * 2;
   rgba[RCOMP] = texel[0];
   rgba[GCOMP] = texel[0];
   rgba[BCOMP] = texel[0];
   rgba[ACOMP] = texel[1];
}


static void
fetch_r5g6b5(const struct gl_texture_image *texImage,
	     GLint i, GLint j, GLint k, GLchan *rgba)
{
   const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
   const GLushort *texel;

   i = i * mml->wScale;
   j = j * mml->hScale;

   texel = ((GLushort *) texImage->Data) + j * mml->width + i;
   rgba[RCOMP] = FX_rgb_scale_5[(*texel >> 11) & 0x1F];
   rgba[GCOMP] = FX_rgb_scale_6[(*texel >>  5) & 0x3F];
   rgba[BCOMP] = FX_rgb_scale_5[ *texel        & 0x1F];
   rgba[ACOMP] = 255;
}


static void
fetch_r4g4b4a4(const struct gl_texture_image *texImage,
	       GLint i, GLint j, GLint k, GLchan *rgba)
{
   const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
   const GLushort *texel;

   i = i * mml->wScale;
   j = j * mml->hScale;

   texel = ((GLushort *) texImage->Data) + j * mml->width + i;
   rgba[RCOMP] = FX_rgb_scale_4[(*texel >>  8) & 0xF];
   rgba[GCOMP] = FX_rgb_scale_4[(*texel >>  4) & 0xF];
   rgba[BCOMP] = FX_rgb_scale_4[ *texel        & 0xF];
   rgba[ACOMP] = FX_rgb_scale_4[(*texel >> 12) & 0xF];
}


static void
fetch_r5g5b5a1(const struct gl_texture_image *texImage,
	       GLint i, GLint j, GLint k, GLchan *rgba)
{
   const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
   const GLushort *texel;

   i = i * mml->wScale;
   j = j * mml->hScale;

   texel = ((GLushort *) texImage->Data) + j * mml->width + i;
   rgba[RCOMP] = FX_rgb_scale_5[(*texel >> 10) & 0x1F];
   rgba[GCOMP] = FX_rgb_scale_5[(*texel >>  5) & 0x1F];
   rgba[BCOMP] = FX_rgb_scale_5[ *texel        & 0x1F];
   rgba[ACOMP] = (*texel >> 15) * 255;
}


static void
fetch_a8r8g8b8(const struct gl_texture_image *texImage,
	       GLint i, GLint j, GLint k, GLchan *rgba)
{
    const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
    const GLuint *texel;

    i = i * mml->wScale;
    j = j * mml->hScale;

    texel = ((GLuint *) texImage->Data) + j * mml->width + i;
    rgba[RCOMP] = (((*texel) >> 16) & 0xff);
    rgba[GCOMP] = (((*texel) >>  8) & 0xff);
    rgba[BCOMP] = (((*texel)      ) & 0xff);
    rgba[ACOMP] = (((*texel) >> 24) & 0xff);
}


static void
fetch_rgb_fxt1(const struct gl_texture_image *texImage,
	       GLint i, GLint j, GLint k, GLchan *rgba)
{
    const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);

    i = i * mml->wScale;
    j = j * mml->hScale;

    fxt1_decode_1(texImage->Data, mml->width, i, j, rgba);
    rgba[ACOMP] = 255;
}


static void
fetch_rgba_fxt1(const struct gl_texture_image *texImage,
		GLint i, GLint j, GLint k, GLchan *rgba)
{
    const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);

    i = i * mml->wScale;
    j = j * mml->hScale;

    fxt1_decode_1(texImage->Data, mml->width, i, j, rgba);
}


static void
fetch_rgb_dxt1(const struct gl_texture_image *texImage,
	       GLint i, GLint j, GLint k, GLchan *rgba)
{
    const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);

    i = i * mml->wScale;
    j = j * mml->hScale;

    _mesa_texformat_rgb_dxt1.FetchTexel2D(texImage, i, j, k, rgba);
}


static void
fetch_rgba_dxt1(const struct gl_texture_image *texImage,
		GLint i, GLint j, GLint k, GLchan *rgba)
{
    const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);

    i = i * mml->wScale;
    j = j * mml->hScale;

    _mesa_texformat_rgba_dxt1.FetchTexel2D(texImage, i, j, k, rgba);
}


static void
fetch_rgba_dxt3(const struct gl_texture_image *texImage,
		GLint i, GLint j, GLint k, GLchan *rgba)
{
    const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);

    i = i * mml->wScale;
    j = j * mml->hScale;

    _mesa_texformat_rgba_dxt3.FetchTexel2D(texImage, i, j, k, rgba);
}


static void
fetch_rgba_dxt5(const struct gl_texture_image *texImage,
		GLint i, GLint j, GLint k, GLchan *rgba)
{
    const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);

    i = i * mml->wScale;
    j = j * mml->hScale;

    _mesa_texformat_rgba_dxt5.FetchTexel2D(texImage, i, j, k, rgba);
}


#if 0 /* break glass in case of emergency */
static void
PrintTexture(int w, int h, int c, const GLubyte * data)
{
   int i, j;
   for (i = 0; i < h; i++) {
      for (j = 0; j < w; j++) {
	 if (c == 2)
	    fprintf(stderr, "%02x %02x  ", data[0], data[1]);
	 else if (c == 3)
	    fprintf(stderr, "%02x %02x %02x  ", data[0], data[1], data[2]);
	 data += c;
      }
      fprintf(stderr, "\n");
   }
}
#endif


gl_format
fxDDChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
                         GLenum srcFormat, GLenum srcType )
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   GLboolean allow32bpt = fxMesa->HaveTexFmt;

   if (TDFX_DEBUG & VERBOSE_TEXTURE) {
      fprintf(stderr, "fxDDChooseTextureFormat(...)\n");
   }

   switch (internalFormat) {
   case GL_COMPRESSED_RGB:
     /* intentional fall through */
   case 3:
   case GL_RGB:
     if ( srcFormat == GL_RGB && srcType == GL_UNSIGNED_SHORT_5_6_5 ) {
       return MESA_FORMAT_RGB565;
     }
     /* intentional fall through */
   case GL_RGB8:
   case GL_RGB10:
   case GL_RGB12:
   case GL_RGB16:
      return (allow32bpt) ? MESA_FORMAT_ARGB8888
                          : MESA_FORMAT_RGB565;
   case GL_RGBA2:
   case GL_RGBA4:
      return MESA_FORMAT_ARGB4444;
   case GL_COMPRESSED_RGBA:
     /* intentional fall through */
   case 4:
   case GL_RGBA:
     if ( srcFormat == GL_BGRA ) {
       if ( srcType == GL_UNSIGNED_INT_8_8_8_8_REV ) {
         return MESA_FORMAT_ARGB8888;
       }
       else if ( srcType == GL_UNSIGNED_SHORT_4_4_4_4_REV ) {
         return MESA_FORMAT_ARGB4444;
       }
       else if ( srcType == GL_UNSIGNED_SHORT_1_5_5_5_REV ) {
         return MESA_FORMAT_ARGB1555;
       }
     }
     /* intentional fall through */
   case GL_RGBA8:
   case GL_RGB10_A2:
   case GL_RGBA12:
   case GL_RGBA16:
      return (allow32bpt) ? MESA_FORMAT_ARGB8888
                          : MESA_FORMAT_ARGB4444;
   case GL_INTENSITY:
   case GL_INTENSITY4:
   case GL_INTENSITY8:
   case GL_INTENSITY12:
   case GL_INTENSITY16:
   case GL_COMPRESSED_INTENSITY:
      return MESA_FORMAT_I8;
   case 1:
   case GL_LUMINANCE:
   case GL_LUMINANCE4:
   case GL_LUMINANCE8:
   case GL_LUMINANCE12:
   case GL_LUMINANCE16:
   case GL_COMPRESSED_LUMINANCE:
      return MESA_FORMAT_L8;
   case GL_ALPHA:
   case GL_ALPHA4:
   case GL_ALPHA8:
   case GL_ALPHA12:
   case GL_ALPHA16:
   case GL_COMPRESSED_ALPHA:
      return MESA_FORMAT_A8;
   case GL_COLOR_INDEX:
   case GL_COLOR_INDEX1_EXT:
   case GL_COLOR_INDEX2_EXT:
   case GL_COLOR_INDEX4_EXT:
   case GL_COLOR_INDEX8_EXT:
   case GL_COLOR_INDEX12_EXT:
   case GL_COLOR_INDEX16_EXT:
      return MESA_FORMAT_CI8;
   case 2:
   case GL_LUMINANCE_ALPHA:
   case GL_LUMINANCE4_ALPHA4:
   case GL_LUMINANCE6_ALPHA2:
   case GL_LUMINANCE8_ALPHA8:
   case GL_LUMINANCE12_ALPHA4:
   case GL_LUMINANCE12_ALPHA12:
   case GL_LUMINANCE16_ALPHA16:
   case GL_COMPRESSED_LUMINANCE_ALPHA:
      return MESA_FORMAT_AL88;
   case GL_R3_G3_B2:
   case GL_RGB4:
   case GL_RGB5:
      return MESA_FORMAT_RGB565;
   case GL_RGB5_A1:
      return MESA_FORMAT_ARGB1555;
   /* GL_EXT_texture_compression_s3tc */
   /* GL_S3_s3tc */
   case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
   case GL_RGB_S3TC:
   case GL_RGB4_S3TC:
      return MESA_FORMAT_RGB_DXT1;
   case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
      return MESA_FORMAT_RGBA_DXT1;
   case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
   case GL_RGBA_S3TC:
   case GL_RGBA4_S3TC:
      return MESA_FORMAT_RGBA_DXT3;
   case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
      return MESA_FORMAT_RGBA_DXT5;
   /* GL_3DFX_texture_compression_FXT1 */
   case GL_COMPRESSED_RGB_FXT1_3DFX:
      return MESA_FORMAT_RGB_FXT1;
   case GL_COMPRESSED_RGBA_FXT1_3DFX:
      return MESA_FORMAT_RGBA_FXT1;
   default:
      _mesa_problem(NULL, "unexpected format in fxDDChooseTextureFormat");
      return MESA_FORMAT_NONE;
   }
}


static GrTextureFormat_t
fxGlideFormat(GLint mesaFormat)
{
   switch (mesaFormat) {
   case MESA_FORMAT_I8:
      return GR_TEXFMT_ALPHA_8;
   case MESA_FORMAT_A8:
      return GR_TEXFMT_ALPHA_8;
   case MESA_FORMAT_L8:
      return GR_TEXFMT_INTENSITY_8;
   case MESA_FORMAT_CI8:
      return GR_TEXFMT_P_8;
   case MESA_FORMAT_AL88:
      return GR_TEXFMT_ALPHA_INTENSITY_88;
   case MESA_FORMAT_RGB565:
      return GR_TEXFMT_RGB_565;
   case MESA_FORMAT_ARGB4444:
      return GR_TEXFMT_ARGB_4444;
   case MESA_FORMAT_ARGB1555:
      return GR_TEXFMT_ARGB_1555;
   case MESA_FORMAT_ARGB8888:
      return GR_TEXFMT_ARGB_8888;
   case MESA_FORMAT_RGB_FXT1:
   case MESA_FORMAT_RGBA_FXT1:
     return GR_TEXFMT_ARGB_CMP_FXT1;
   case MESA_FORMAT_RGB_DXT1:
   case MESA_FORMAT_RGBA_DXT1:
     return GR_TEXFMT_ARGB_CMP_DXT1;
   case MESA_FORMAT_RGBA_DXT3:
     return GR_TEXFMT_ARGB_CMP_DXT3;
   case MESA_FORMAT_RGBA_DXT5:
     return GR_TEXFMT_ARGB_CMP_DXT5;
   default:
      _mesa_problem(NULL, "Unexpected format in fxGlideFormat");
      return 0;
   }
}


static FetchTexelFuncC
fxFetchFunction(GLint mesaFormat)
{
   switch (mesaFormat) {
   case MESA_FORMAT_I8:
      return &fetch_intensity8;
   case MESA_FORMAT_A8:
      return &fetch_alpha8;
   case MESA_FORMAT_L8:
      return &fetch_luminance8;
   case MESA_FORMAT_CI8:
      return &fetch_index8;
   case MESA_FORMAT_AL88:
      return &fetch_luminance8_alpha8;
   case MESA_FORMAT_RGB565:
      return &fetch_r5g6b5;
   case MESA_FORMAT_ARGB4444:
      return &fetch_r4g4b4a4;
   case MESA_FORMAT_ARGB1555:
      return &fetch_r5g5b5a1;
   case MESA_FORMAT_ARGB8888:
      return &fetch_a8r8g8b8;
   case MESA_FORMAT_RGB_FXT1:
      return &fetch_rgb_fxt1;
   case MESA_FORMAT_RGBA_FXT1:
      return &fetch_rgba_fxt1;
   case MESA_FORMAT_RGB_DXT1:
      return &fetch_rgb_dxt1;
   case MESA_FORMAT_RGBA_DXT1:
      return &fetch_rgba_dxt1;
   case MESA_FORMAT_RGBA_DXT3:
      return &fetch_rgba_dxt3;
   case MESA_FORMAT_RGBA_DXT5:
      return &fetch_rgba_dxt5;
   default:
      _mesa_problem(NULL, "Unexpected format in fxFetchFunction");
      return NULL;
   }
}


static GLboolean
adjust2DRatio (GLcontext *ctx,
	       GLint xoffset, GLint yoffset,
	       GLint width, GLint height,
	       GLenum format, GLenum type, const GLvoid *pixels,
	       const struct gl_pixelstore_attrib *packing,
	       tfxMipMapLevel *mml,
	       struct gl_texture_image *texImage,
	       GLint texelBytes,
	       GLint dstRowStride)
{
   const GLint newWidth = width * mml->wScale;
   const GLint newHeight = height * mml->hScale;
   GLvoid *tempImage;

   if (!_mesa_is_format_compressed(texImage->TexFormat)) {
      GLubyte *destAddr;

      tempImage = MALLOC(width * height * texelBytes);
      if (!tempImage) {
         return GL_FALSE;
      }

      _mesa_texstore(ctx, 2, texImage->_BaseFormat,
                     texImage->TexFormat, tempImage,
                     0, 0, 0, /* dstX/Y/Zoffset */
                     width * texelBytes, /* dstRowStride */
                     0, /* dstImageStride */
                     width, height, 1,
                     format, type, pixels, packing);

      /* now rescale */
      /* compute address of dest subimage within the overal tex image */
      destAddr = (GLubyte *) texImage->Data
         + (yoffset * mml->hScale * mml->width
            + xoffset * mml->wScale) * texelBytes;

      _mesa_rescale_teximage2d(texelBytes,
                               width,
                               dstRowStride, /* dst stride */
                               width, height,
                               newWidth, newHeight,
                               tempImage, destAddr);
   } else {
      const GLint rawBytes = 4;
      GLvoid *rawImage = MALLOC(width * height * rawBytes);

      if (!rawImage) {
         return GL_FALSE;
      }
      tempImage = MALLOC(newWidth * newHeight * rawBytes);
      if (!tempImage) {
         return GL_FALSE;
      }
      /* unpack image, apply transfer ops and store in rawImage */
      _mesa_texstore(ctx, 2, GL_RGBA,
                     &_mesa_texformat_rgba8888_rev, rawImage,
                     0, 0, 0, /* dstX/Y/Zoffset */
                     width * rawBytes, /* dstRowStride */
                     0, /* dstImageStride */
                     width, height, 1,
                     format, type, pixels, packing);
      _mesa_rescale_teximage2d(rawBytes,
                               width,
                               newWidth * rawBytes, /* dst stride */
                               width, height, /* src */
                               newWidth, newHeight, /* dst */
                               rawImage /*src*/, tempImage /*dst*/ );
      _mesa_texstore(ctx, 2, texImage->_BaseFormat,
                     texImage->TexFormat, texImage->Data,
                     xoffset * mml->wScale, yoffset * mml->hScale, 0, /* dstX/Y/Zoffset */
                     dstRowStride,
                     0, /* dstImageStride */
                     newWidth, newHeight, 1,
                     GL_RGBA, CHAN_TYPE, tempImage, &ctx->DefaultPacking);
      FREE(rawImage);
   }

   FREE(tempImage);

   return GL_TRUE;
}


void
fxDDTexImage2D(GLcontext * ctx, GLenum target, GLint level,
	       GLint internalFormat, GLint width, GLint height, GLint border,
	       GLenum format, GLenum type, const GLvoid * pixels,
	       const struct gl_pixelstore_attrib *packing,
	       struct gl_texture_object *texObj,
	       struct gl_texture_image *texImage)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxTexInfo *ti;
   tfxMipMapLevel *mml;
   GLint texelBytes, dstRowStride;

   if (TDFX_DEBUG & VERBOSE_TEXTURE) {
       fprintf(stderr, "fxDDTexImage2D: id=%d int 0x%x  format 0x%x  type 0x%x  %dx%d\n",
                       texObj->Name, texImage->InternalFormat, format, type,
                       texImage->Width, texImage->Height);
   }

   if (!fxIsTexSupported(target, internalFormat, texImage)) {
      _mesa_problem(NULL, "fx Driver: unsupported texture in fxDDTexImg()\n");
      return;
   }

   if (!texObj->DriverData) {
      texObj->DriverData = fxAllocTexObjData(fxMesa);
      if (!texObj->DriverData) {
         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
         return;
      }
   }
   ti = fxTMGetTexInfo(texObj);

   if (!texImage->DriverData) {
      texImage->DriverData = CALLOC(sizeof(tfxMipMapLevel));
      if (!texImage->DriverData) {
         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
         return;
      }
   }
   mml = FX_MIPMAP_DATA(texImage);

   fxTexGetInfo(width, height, NULL, NULL, NULL, NULL,
		&mml->wScale, &mml->hScale);

   mml->width = width * mml->wScale;
   mml->height = height * mml->hScale;

#if FX_COMPRESS_S3TC_AS_FXT1_HACK
   /* [koolsmoky] substitute FXT1 for DXTn and Legacy S3TC */
   if (!ctx->Mesa_DXTn && _mesa_is_format_compressed(texImage->TexFormat)) {
     switch (internalFormat) {
     case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
     case GL_RGB_S3TC:
     case GL_RGB4_S3TC:
       internalFormat = GL_COMPRESSED_RGB_FXT1_3DFX;
       break;
     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
     case GL_RGBA_S3TC:
     case GL_RGBA4_S3TC:
       internalFormat = GL_COMPRESSED_RGBA_FXT1_3DFX;
     }
     texImage->InternalFormat = internalFormat;
   }
#endif
#if FX_TC_NAPALM
   if (fxMesa->type >= GR_SSTTYPE_Voodoo4) {
      GLenum texNapalm = 0;
      if (internalFormat == GL_COMPRESSED_RGB) {
         texNapalm = GL_COMPRESSED_RGB_FXT1_3DFX;
      } else if (internalFormat == GL_COMPRESSED_RGBA) {
         texNapalm = GL_COMPRESSED_RGBA_FXT1_3DFX;
      }
      if (texNapalm) {
         texImage->InternalFormat = internalFormat = texNapalm;
      }
   }
#endif

   texelBytes = _mesa_get_format_bytes(texImage->TexFormat->MesaFormat);
   /*if (!fxMesa->HaveTexFmt) assert(texelBytes == 1 || texelBytes == 2);*/

   mml->glideFormat = fxGlideFormat(texImage->TexFormat->MesaFormat);

   /* allocate mipmap buffer */
   assert(!texImage->Data);
   if (_mesa_is_format_compressed(texImage->TexFormat)) {
      texImage->CompressedSize = _mesa_format_image_size(texImage->TexFormat,
                                                         mml->width,
                                                         mml->height, 1);
      dstRowStride = _mesa_format_row_stride(texImage->TexFormat, mml->width);
      texImage->Data = _mesa_malloc(texImage->CompressedSize);
   } else {
      dstRowStride = mml->width * texelBytes;
      texImage->Data = _mesa_malloc(mml->width * mml->height * texelBytes);
   }
   if (!texImage->Data) {
      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
      return;
   }

   if (pixels != NULL) {
      if (mml->wScale != 1 || mml->hScale != 1) {
         /* rescale image to overcome 1:8 aspect limitation */
         if (!adjust2DRatio(ctx,
			    0, 0,
			    width, height,
			    format, type, pixels,
			    packing,
			    mml,
			    texImage,
			    texelBytes,
			    dstRowStride)
            ) {
            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
            return;
         }
      }
      else {
         /* no rescaling needed */
         /* unpack image, apply transfer ops and store in texImage->Data */
         _mesa_texstore(ctx, 2, texImage->_BaseFormat,
                        texImage->TexFormat, texImage->Data,
                        0, 0, 0, /* dstX/Y/Zoffset */
                        dstRowStride,
                        0, /* dstImageStride */
                        width, height, 1,
                        format, type, pixels, packing);
      }

      /* GL_SGIS_generate_mipmap */
      if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
         GLint mipWidth, mipHeight;
         tfxMipMapLevel *mip;
         struct gl_texture_image *mipImage;
         const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
         const GLint maxLevels = _mesa_max_texture_levels(ctx, texObj->Target);

         assert(!_mesa_is_format_compressed(texImage->TexFormat));

         while (level < texObj->MaxLevel && level < maxLevels - 1) {
            mipWidth = width / 2;
            if (!mipWidth) {
               mipWidth = 1;
            }
            mipHeight = height / 2;
            if (!mipHeight) {
               mipHeight = 1;
            }
            if ((mipWidth == width) && (mipHeight == height)) {
               break;
            }
            _mesa_TexImage2D(target, ++level, internalFormat,
                             mipWidth, mipHeight, border,
                             format, type,
                             NULL);
            mipImage = _mesa_select_tex_image(ctx, texUnit, target, level);
            mip = FX_MIPMAP_DATA(mipImage);
            _mesa_halve2x2_teximage2d(ctx,
                                      texImage,
                                      texelBytes,
                                      mml->width, mml->height,
                                      texImage->Data, mipImage->Data);
            texImage = mipImage;
            mml = mip;
            width = mipWidth;
            height = mipHeight;
         }
      }
   }

   ti->info.format = mml->glideFormat;
   texImage->FetchTexelc = fxFetchFunction(texImage->TexFormat->MesaFormat);

   fxTexInvalidate(ctx, texObj);
}


void
fxDDTexSubImage2D(GLcontext * ctx, GLenum target, GLint level,
		  GLint xoffset, GLint yoffset,
		  GLsizei width, GLsizei height,
		  GLenum format, GLenum type, const GLvoid * pixels,
		  const struct gl_pixelstore_attrib *packing,
		  struct gl_texture_object *texObj,
		  struct gl_texture_image *texImage)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxTexInfo *ti;
   tfxMipMapLevel *mml;
   GLint texelBytes, dstRowStride;

   if (TDFX_DEBUG & VERBOSE_TEXTURE) {
       fprintf(stderr, "fxDDTexSubImage2D: id=%d\n", texObj->Name);
   }

   if (!texObj->DriverData) {
      _mesa_problem(ctx, "problem in fxDDTexSubImage2D");
      return;
   }

   ti = fxTMGetTexInfo(texObj);
   assert(ti);
   mml = FX_MIPMAP_DATA(texImage);
   assert(mml);

   assert(texImage->Data);	/* must have an existing texture image! */
   assert(texImage->_BaseFormat);

   texelBytes = _mesa_get_format_bytes(texImage->TexFormat->MesaFormat);
   if (_mesa_is_format_compressed(texImage->TexFormat)) {
      dstRowStride = _mesa_format_row_stride(texImage->TexFormat, mml->width);
   } else {
      dstRowStride = mml->width * texelBytes;
   }

   if (mml->wScale != 1 || mml->hScale != 1) {
      /* need to rescale subimage to match mipmap level's rescale factors */
      if (!adjust2DRatio(ctx,
			 xoffset, yoffset,
			 width, height,
			 format, type, pixels,
			 packing,
			 mml,
			 texImage,
			 texelBytes,
			 dstRowStride)
         ) {
         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage2D");
         return;
      }
   }
   else {
      /* no rescaling needed */
      _mesa_texstore(ctx, 2, texImage->_BaseFormat,
                     texImage->TexFormat, (GLubyte *) texImage->Data,
                     xoffset, yoffset, 0, /* dstX/Y/Zoffset */
                     dstRowStride,
                     0, /* dstImageStride */
                     width, height, 1,
                     format, type, pixels, packing);
   }

   /* GL_SGIS_generate_mipmap */
   if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
      GLint mipWidth, mipHeight;
      tfxMipMapLevel *mip;
      struct gl_texture_image *mipImage;
      const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
      const GLint maxLevels = _mesa_max_texture_levels(ctx, texObj->Target);

      assert(!_mesa_is_format_compressed(texImage->TexFormat));

      width = texImage->Width;
      height = texImage->Height;
      while (level < texObj->MaxLevel && level < maxLevels - 1) {
         mipWidth = width / 2;
         if (!mipWidth) {
            mipWidth = 1;
         }
         mipHeight = height / 2;
         if (!mipHeight) {
            mipHeight = 1;
         }
         if ((mipWidth == width) && (mipHeight == height)) {
            break;
         }
         ++level;
         mipImage = _mesa_select_tex_image(ctx, texUnit, target, level);
         mip = FX_MIPMAP_DATA(mipImage);
         _mesa_halve2x2_teximage2d(ctx,
                                   texImage,
                                   texelBytes,
                                   mml->width, mml->height,
                                   texImage->Data, mipImage->Data);
         texImage = mipImage;
         mml = mip;
         width = mipWidth;
         height = mipHeight;
      }
   }

   if (ti->validated && ti->isInTM && !texObj->GenerateMipmap)
      fxTMReloadMipMapLevel(fxMesa, texObj, level);
   else
      fxTexInvalidate(ctx, texObj);
}


void
fxDDCompressedTexImage2D (GLcontext *ctx, GLenum target,
                          GLint level, GLint internalFormat,
                          GLsizei width, GLsizei height, GLint border,
                          GLsizei imageSize, const GLvoid *data,
                          struct gl_texture_object *texObj,
                          struct gl_texture_image *texImage)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxTexInfo *ti;
   tfxMipMapLevel *mml;

   if (TDFX_DEBUG & VERBOSE_TEXTURE) {
       fprintf(stderr, "fxDDCompressedTexImage2D: id=%d int 0x%x  %dx%d\n",
                       texObj->Name, internalFormat,
                       width, height);
   }

   assert(_mesa_is_format_compressed(texImage->TexFormat));

   if (!fxIsTexSupported(target, internalFormat, texImage)) {
      _mesa_problem(NULL, "fx Driver: unsupported texture in fxDDCompressedTexImg()\n");
      return;
   }

   if (!texObj->DriverData) {
      texObj->DriverData = fxAllocTexObjData(fxMesa);
      if (!texObj->DriverData) {
         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage2D");
         return;
      }
   }
   ti = fxTMGetTexInfo(texObj);

   if (!texImage->DriverData) {
      texImage->DriverData = CALLOC(sizeof(tfxMipMapLevel));
      if (!texImage->DriverData) {
         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage2D");
         return;
      }
   }
   mml = FX_MIPMAP_DATA(texImage);

   fxTexGetInfo(width, height, NULL, NULL, NULL, NULL,
		&mml->wScale, &mml->hScale);

   mml->width = width * mml->wScale;
   mml->height = height * mml->hScale;

   /* Determine the appropriate Glide texel format,
    * given the user's internal texture format hint.
    */
   mml->glideFormat = fxGlideFormat(texImage->TexFormat->MesaFormat);

   /* allocate new storage for texture image, if needed */
   if (!texImage->Data) {
      texImage->CompressedSize = _mesa_format_image_size(texImage->TexFormat,
                                                         mml->width,
                                                         mml->height, 1);
      texImage->Data = _mesa_malloc(texImage->CompressedSize);
      if (!texImage->Data) {
         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage2D");
         return;
      }
   }

   /* save the texture data */
   if (mml->wScale != 1 || mml->hScale != 1) {
      /* [dBorca] Hack alert:
       * now we're screwed. We can't decompress,
       * unless we do it in HW (via textureBuffer).
       * We still have some chances:
       * 1) we got FXT1 textures - we CAN decompress, rescale for
       *    aspectratio, then compress back.
       * 2) there is a chance that MIN("s", "t") won't be overflowed.
       *    Thus, we don't care about textureclamp and we could lower
       *    MIN("uscale", "vscale") below 32. We still have to have
       *    our data aligned inside a 8:1 rectangle.
       * 3) just in case if MIN("s", "t") gets overflowed with GL_REPEAT,
       *    we replicate the data over the padded area.
       * For now, we take 2) + 3) but texelfetchers will be wrong!
       */
      GLuint srcRowStride = _mesa_format_row_stride(texImage->TexFormat, width);

      GLuint destRowStride = _mesa_format_row_stride(texImage->TexFormat,
                                                     mml->width);

      _mesa_upscale_teximage2d(srcRowStride, (height+3) / 4,
                               destRowStride, (mml->height+3) / 4,
                               1, data, srcRowStride,
                               texImage->Data);
      ti->padded = GL_TRUE;
   } else {
      MEMCPY(texImage->Data, data, texImage->CompressedSize);
   }

   ti->info.format = mml->glideFormat;
   texImage->FetchTexelc = fxFetchFunction(texImage->TexFormat->MesaFormat);

   /* GL_SGIS_generate_mipmap */
   if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
      assert(!_mesa_is_format_compressed(texImage->TexFormat));
   }

   fxTexInvalidate(ctx, texObj);
}


void
fxDDCompressedTexSubImage2D( GLcontext *ctx, GLenum target,
                             GLint level, GLint xoffset,
                             GLint yoffset, GLsizei width,
                             GLint height, GLenum format,
                             GLsizei imageSize, const GLvoid *data,
                             struct gl_texture_object *texObj,
                             struct gl_texture_image *texImage )
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxTexInfo *ti;
   tfxMipMapLevel *mml;
   GLint destRowStride, srcRowStride;
   GLint i, rows;
   GLubyte *dest;

   if (TDFX_DEBUG & VERBOSE_TEXTURE) {
       fprintf(stderr, "fxDDCompressedTexSubImage2D: id=%d\n", texObj->Name);
   }

   ti = fxTMGetTexInfo(texObj);
   assert(ti);
   mml = FX_MIPMAP_DATA(texImage);
   assert(mml);

   srcRowStride = _mesa_format_row_stride(texImage->TexFormat, width);

   destRowStride = _mesa_format_row_stride(texImage->TexFormat, mml->width);
   dest = _mesa_compressed_image_address(xoffset, yoffset, 0,
                                         texImage->InternalFormat,
                                         mml->width,
                              (GLubyte*) texImage->Data);

   rows = height / 4; /* hardcoded 4, but works for FXT1/DXTC */

   for (i = 0; i < rows; i++) {
      MEMCPY(dest, data, srcRowStride);
      dest += destRowStride;
      data = (GLvoid *)((GLuint)data + (GLuint)srcRowStride);
   }

   /* [dBorca] Hack alert:
    * see fxDDCompressedTexImage2D for caveats
    */
   if (mml->wScale != 1 || mml->hScale != 1) {
      srcRowStride = _mesa_format_row_stride(texImage->TexFormat, texImage->Width);

      destRowStride = _mesa_format_row_stride(texImage->TexFormat, mml->width);
      _mesa_upscale_teximage2d(srcRowStride, texImage->Height / 4,
                               destRowStride, mml->height / 4,
                               1, texImage->Data, destRowStride,
                               texImage->Data);
   }

   /* GL_SGIS_generate_mipmap */
   if (level == texObj->BaseLevel && texObj->GenerateMipmap) {
      assert(!_mesa_is_format_compressed(texImage->TexFormat));
   }

   if (ti->validated && ti->isInTM)
      fxTMReloadMipMapLevel(fxMesa, texObj, level);
   else
      fxTexInvalidate(ctx, texObj);
}


void
fxDDTexImage1D (GLcontext *ctx, GLenum target, GLint level,
	        GLint internalFormat, GLint width, GLint border,
	        GLenum format, GLenum type, const GLvoid *pixels,
	        const struct gl_pixelstore_attrib *packing,
	        struct gl_texture_object *texObj,
	        struct gl_texture_image *texImage)
{
 fxDDTexImage2D(ctx, target, level,
	        internalFormat, width, 1, border,
	        format, type, pixels,
	        packing,
	        texObj,
	        texImage);
}


void
fxDDTexSubImage1D(GLcontext * ctx, GLenum target, GLint level,
		  GLint xoffset,
		  GLsizei width,
		  GLenum format, GLenum type, const GLvoid * pixels,
		  const struct gl_pixelstore_attrib *packing,
		  struct gl_texture_object *texObj,
		  struct gl_texture_image *texImage)
{
 fxDDTexSubImage2D(ctx, target, level,
		   xoffset, 0, width, 1,
		   format, type, pixels,
		   packing,
		   texObj,
		   texImage);
}


GLboolean
fxDDTestProxyTexImage (GLcontext *ctx, GLenum target,
                       GLint level, GLint internalFormat,
                       GLenum format, GLenum type,
                       GLint width, GLint height,
                       GLint depth, GLint border)
{
 /* XXX todo - maybe through fxTexValidate() */
 return _mesa_test_proxy_teximage(ctx, target,
                                  level, internalFormat,
                                  format, type,
                                  width, height,
                                  depth, border);
}


#else /* FX */

/*
 * Need this to provide at least one external definition.
 */

extern int gl_fx_dummy_function_ddtex(void);
int
gl_fx_dummy_function_ddtex(void)
{
   return 0;
}

#endif /* FX */
