/* -*- mode: C; tab-width:8; c-basic-offset:2 -*- */

/*
 * Mesa 3-D graphics library
 * Version:  3.3
 *
 * Copyright (C) 1999-2000  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.
 *
 *
 * Original Mesa / 3Dfx device driver (C) 1999 David Bucciarelli, by the
 * terms stated above.
 *
 * Thank you for your contribution, David!
 *
 * Please make note of the above copyright/license statement.  If you
 * contributed code or bug fixes to this code under the previous (GNU
 * Library) license and object to the new license, your code will be
 * removed at your request.  Please see the Mesa docs/COPYRIGHT file
 * for more information.
 *
 * Additional Mesa/3Dfx driver developers:
 *   Daryll Strauss <daryll@precisioninsight.com>
 *   Keith Whitwell <keith@precisioninsight.com>
 *
 * See fxapi.h for more revision/author details.
 */


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

#if defined(FX)

#include "fxdrv.h"
#include "image.h"
#include "texutil.h"
#include "texformat.h"


static GLint
bytesPerTexel(GLint mesaFormat)
{
   switch (mesaFormat) {
   case MESA_FORMAT_RGBA8888:
      return 4;
   case MESA_FORMAT_ABGR8888:
      return 4;
   case MESA_FORMAT_ARGB8888:
      return 4;
   case MESA_FORMAT_RGB888:
      return 4;
   case MESA_FORMAT_BGR888:
      return 4;
   case MESA_FORMAT_RGB565:
      return 2;
   case MESA_FORMAT_ARGB4444:
      return 2;
   case MESA_FORMAT_ARGB1555:
      return 2;
   case MESA_FORMAT_AL88:
      return 2;
   case MESA_FORMAT_RGB332:
      return 1;
   case MESA_FORMAT_A8:
      return 1;
   case MESA_FORMAT_L8:
      return 1;
   case MESA_FORMAT_I8:
      return 1;
   case MESA_FORMAT_CI8:
      return 1;
   default:
      return 0;
   }
}


/*
 * This is a bit of a hack.  We just need it for Mesa 3.4.2 in order
 * to keep the FX driver working.  This is all different in 3.5
 */
static GLboolean
_mesa_convert_teximage(GLint mesaFormat, GLint dstWidth, GLint dstHeight,
                       void *destPtr, GLint dstStride,
                       GLint srcWidth, GLint srcHeight,
                       GLenum format, GLenum type, const GLvoid *srcPtr,
                       const struct gl_pixelstore_attrib *packing)
{
   if (srcWidth != dstWidth || srcHeight != dstHeight) {
      /* rescale (needed because of Glide's 1:8 texture aspect limitation) */
      GLvoid *tmpImage;
      GLint texelBytes;
      GLboolean b;

      assert(dstWidth >= srcWidth);
      assert(dstHeight >= dstHeight);

      texelBytes = _mesa_bytes_per_pixel(format, type);
      tmpImage = MALLOC(dstWidth * dstHeight * texelBytes);
      if (!tmpImage) {
         return GL_FALSE;  /* out of mem */
      }
      b = _mesa_convert_texsubimage2d(mesaFormat,
                                      0, 0,  /* xoffset, yoffset */
                                      srcWidth, srcHeight,
                                      dstWidth,
                                      format, type,
                                      packing,
                                      srcPtr, tmpImage);
      if (!b) {
         /* can't handle this pixel format/type/packing combination */
         FREE(tmpImage);
         return GL_FALSE;
      }      

      texelBytes = bytesPerTexel(mesaFormat);
      if (!texelBytes) {
         FREE(tmpImage);
         return GL_FALSE;
      }

      /* rescale now */
      _mesa_rescale_teximage2d(texelBytes, srcWidth, srcHeight,
                               dstWidth, dstHeight, tmpImage, destPtr);
      FREE(tmpImage);
      return GL_TRUE;
   }
   else {
      /* rescale not needed */
      return _mesa_convert_texsubimage2d(mesaFormat,
                                         0, 0,  /* xoffset, yoffset */
                                         dstWidth, dstHeight,
                                         dstWidth,
                                         format, type,
                                         packing,
                                         srcPtr, destPtr);
   }
}


static GLboolean
_mesa_convert_texsubimage(GLint mesaFormat, GLint xoffset, GLint yoffset,
                          GLint dstWidth, GLint dstHeight,
                          void *destPtr, GLint dstStride,
                          GLint subWidth, GLint subHeight,
                          GLint srcWidth, GLint srcHeight,
                          GLenum format, GLenum type, const GLvoid *srcPtr,
                          const struct gl_pixelstore_attrib *packing)
{
   if (dstWidth != srcWidth || dstHeight != srcHeight) {
      /* bail out on texsubimage changes that need rescale.  TexImage2D
       * will get called instead.
       */
      return GL_FALSE;
   }
   else {
      return _mesa_convert_texsubimage2d(mesaFormat,
                                         xoffset, yoffset,
                                         subWidth, subHeight,
                                         dstWidth,
                                         format, type,
                                         packing,
                                         srcPtr, destPtr);
   }
}


static void
_mesa_unconvert_teximage(GLint mesaFormat, GLint srcWidth, GLint srcHeight,
                         const GLvoid *srcPtr,
                         GLint srcStride, GLint dstWidth, GLint dstHeight,
                         GLenum dstFormat, GLvoid *dstPtr)
{
   if (srcWidth != dstWidth || srcHeight != dstHeight) {
      /* need to rescale image */
      GLint texelBytes = bytesPerTexel(mesaFormat);
      GLvoid *tmpImage;

      if (!texelBytes)
         return;  /* bad format? */

      tmpImage = MALLOC(srcWidth * srcHeight * texelBytes);
      if (!tmpImage) {
         return;
      }

      /* convert */
      _mesa_unconvert_teximage2d(mesaFormat, dstFormat, srcWidth, srcHeight,
                                 srcPtr, tmpImage);
      /* rescale */
      _mesa_rescale_teximage2d(texelBytes, srcWidth, srcHeight,
                               dstWidth, dstHeight, tmpImage, dstPtr);
      FREE(tmpImage);
   }
   else {
      _mesa_unconvert_teximage2d(mesaFormat, dstFormat, srcWidth, srcHeight,
                                 srcPtr, dstPtr);
   }
}


#if 0
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[ti->tObj->BaseLevel]->Width,
	    ti->tObj->Image[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 min %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, "\tInt Scales: s %d t %d\n", 
	  ti->int_sScale/0x800000, ti->int_tScale/0x800000);
  fprintf(stderr, "\t%s\n", (ti->fixedPalette)?"Fixed palette":"Non fixed palette");
  fprintf(stderr, "\t%s\n", (ti->validated)?"Validated":"Not validated");
}
#endif


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

static void fxTexInvalidate(GLcontext *ctx, struct gl_texture_object *tObj)
{
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  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;
  ctx->Driver.RenderStart = fxSetupFXUnits;
}

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

  if(!(ti=CALLOC(sizeof(tfxTexInfo)))) {
    fprintf(stderr,"fx Driver: 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;

  for(i=0;i<MAX_TEXTURE_LEVELS;i++) {
    ti->mipmapLevel[i].data=NULL;
  }

  return ti;
}

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

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

  if(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;
  ctx->Driver.RenderStart = fxSetupFXUnits;
}

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

   if (MESA_VERBOSE&VERBOSE_DRIVER) {
      if(param)
	 fprintf(stderr,"fxmesa: texenv(%x,%x)\n",pname,(GLint)(*param));
      else
	 fprintf(stderr,"fxmesa: texenv(%x)\n",pname);
   }

   /* apply any lod biasing right now */
   if (pname==GL_TEXTURE_LOD_BIAS_EXT) {
     FX_grTexLodBiasValue(GR_TMU0,*param);

     if(fxMesa->haveTwoTMUs) {
       FX_grTexLodBiasValue(GR_TMU1,*param);
     }

   }

   fxMesa->new_state|=FX_NEW_TEXTURING;
   ctx->Driver.RenderStart = fxSetupFXUnits;
}

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

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

  if(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_NEAREST:
      ti->mmMode=GR_MIPMAP_NEAREST;
      ti->minFilt=GR_TEXTUREFILTER_POINT_SAMPLED;
      ti->LODblend=FXFALSE;
      break;
    case GL_LINEAR_MIPMAP_NEAREST:
      ti->mmMode=GR_MIPMAP_NEAREST;
      ti->minFilt=GR_TEXTUREFILTER_BILINEAR;
      ti->LODblend=FXFALSE;
      break;
    case GL_NEAREST_MIPMAP_LINEAR:
      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_LINEAR_MIPMAP_LINEAR:
      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;
    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;
    }
    fxTexInvalidate(ctx,tObj);
    break;

  case GL_TEXTURE_WRAP_S:
    switch(param) {
    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;
    ctx->Driver.RenderStart = fxSetupFXUnits;
    break;

  case GL_TEXTURE_WRAP_T:
    switch(param) {
    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;
    ctx->Driver.RenderStart = fxSetupFXUnits;
    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 (MESA_VERBOSE & VERBOSE_DRIVER) {
     fprintf(stderr, "fxmesa: fxDDTexDel(%d,%p)\n", tObj->Name, ti);
  }

  if (!ti)
    return;

  fxTMFreeTexture(fxMesa, tObj);

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

  ctx->NewState |= NEW_TEXTURING;
}



/*
 * Convert a gl_color_table texture palette to Glide's format.
 */
static void
convertPalette(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->TableType == GL_UNSIGNED_BYTE);

  switch (table->Format) {
    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;
      }
      break;
    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;
      }
      break;
    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;
      }
      break;
    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;
      }
      break;
    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;
      }
      break;
    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;
      }
      break;
  }
}


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

  if (tObj) {
    /* per-texture palette */
    tfxTexInfo *ti;
    if (MESA_VERBOSE & VERBOSE_DRIVER) {
      fprintf(stderr, "fxmesa: fxDDTexPalette(%d,%x)\n",
              tObj->Name, (GLuint) tObj->DriverData);
    }
    if (!tObj->DriverData)
      tObj->DriverData = fxAllocTexObjData(fxMesa);
    ti = fxTMGetTexInfo(tObj);
    convertPalette(ti->palette.data, &tObj->Palette);
    fxTexInvalidate(ctx, tObj);
  }
  else {
    /* global texture palette */
    if (MESA_VERBOSE & VERBOSE_DRIVER) {
      fprintf(stderr, "fxmesa: fxDDTexPalette(global)\n");
    }
    convertPalette(fxMesa->glbPalette.data, &ctx->Texture.Palette);
    fxMesa->new_state |= FX_NEW_TEXTURING;
    ctx->Driver.RenderStart = fxSetupFXUnits;

    FX_grTexDownloadTable(GR_TMU0,GR_TEXTABLE_PALETTE, &(fxMesa->glbPalette));
#if !defined(FX_GLIDE3)
    if (fxMesa->haveTwoTMUs)
       FX_grTexDownloadTable(GR_TMU1, GR_TEXTABLE_PALETTE, &(fxMesa->glbPalette));
#endif
  }
}


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

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

  if (state) {
    fxMesa->haveGlobalPaletteTexture = 1;
  }
  else {
    fxMesa->haveGlobalPaletteTexture = 0;

    if ((ctx->Texture.Unit[0].Current == ctx->Texture.Unit[0].CurrentD[2]) &&
        (ctx->Texture.Unit[0].Current != NULL)) {
      struct gl_texture_object *tObj = ctx->Texture.Unit[0].Current;

      if (!tObj->DriverData)
        tObj->DriverData = fxAllocTexObjData(fxMesa);
  
      fxTexInvalidate(ctx, tObj);
    }
  }
}


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;
  }
}

/* Need different versions for different cpus.
 */
#define INT_TRICK(l2) (0x800000 * l2)


int fxTexGetInfo(int w, int h, GrLOD_t *lodlevel, GrAspectRatio_t *ar,
                 float *sscale, float *tscale,
                 int *i_sscale, int *i_tscale,
                 int *wscale, int *hscale)
{

  static GrLOD_t lod[9]={GR_LOD_256,GR_LOD_128,GR_LOD_64,GR_LOD_32,
                         GR_LOD_16,GR_LOD_8,GR_LOD_4,GR_LOD_2,GR_LOD_1};

  int logw,logh,ws,hs;
  GrLOD_t l;
  GrAspectRatio_t aspectratio;
  float s,t;
  int is,it;

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

  switch(logw-logh) {
  case 0:
    aspectratio=GR_ASPECT_1x1;
    l=lod[8-logw];
    s=t=256.0f;
    is=it=INT_TRICK(8);
    ws=hs=1;
    break;
  case 1:
    aspectratio=GR_ASPECT_2x1;
    l=lod[8-logw];
    s=256.0f;
    t=128.0f;
    is=INT_TRICK(8);it=INT_TRICK(7);
    ws=1;
    hs=1;
    break;
  case 2:
    aspectratio=GR_ASPECT_4x1;
    l=lod[8-logw];
    s=256.0f;
    t=64.0f;
    is=INT_TRICK(8);it=INT_TRICK(6);
    ws=1;
    hs=1;
    break;
  case 3:
    aspectratio=GR_ASPECT_8x1;
    l=lod[8-logw];
    s=256.0f;
    t=32.0f;
    is=INT_TRICK(8);it=INT_TRICK(5);
    ws=1;
    hs=1;
    break;
  case 4:
    aspectratio=GR_ASPECT_8x1;
    l=lod[8-logw];
    s=256.0f;
    t=32.0f;
    is=INT_TRICK(8);it=INT_TRICK(5);
    ws=1;
    hs=2;
    break;
  case 5:
    aspectratio=GR_ASPECT_8x1;
    l=lod[8-logw];
    s=256.0f;
    t=32.0f;
    is=INT_TRICK(8);it=INT_TRICK(5);
    ws=1;
    hs=4;
    break;
  case 6:
    aspectratio=GR_ASPECT_8x1;
    l=lod[8-logw];
    s=256.0f;
    t=32.0f;
    is=INT_TRICK(8);it=INT_TRICK(5);
    ws=1;
    hs=8;
    break;
  case 7:
    aspectratio=GR_ASPECT_8x1;
    l=lod[8-logw];
    s=256.0f;
    t=32.0f;
    is=INT_TRICK(8);it=INT_TRICK(5);
    ws=1;
    hs=16;
    break;
  case 8:
    aspectratio=GR_ASPECT_8x1;
    l=lod[8-logw];
    s=256.0f;
    t=32.0f;
    is=INT_TRICK(8);it=INT_TRICK(5);
    ws=1;
    hs=32;
    break;
  case -1:
    aspectratio=GR_ASPECT_1x2;
    l=lod[8-logh];
    s=128.0f;
    t=256.0f;
    is=INT_TRICK(7);it=INT_TRICK(8);
    ws=1;
    hs=1;
    break;
  case -2:
    aspectratio=GR_ASPECT_1x4;
    l=lod[8-logh];
    s=64.0f;
    t=256.0f;
    is=INT_TRICK(6);it=INT_TRICK(8);
    ws=1;
    hs=1;
    break;
  case -3:
    aspectratio=GR_ASPECT_1x8;
    l=lod[8-logh];
    s=32.0f;
    t=256.0f;
    is=INT_TRICK(5);it=INT_TRICK(8);
    ws=1;
    hs=1;
    break;
  case -4:
    aspectratio=GR_ASPECT_1x8;
    l=lod[8-logh];
    s=32.0f;
    t=256.0f;
    is=INT_TRICK(5);it=INT_TRICK(8);
    ws=2;
    hs=1;
    break;
  case -5:
    aspectratio=GR_ASPECT_1x8;
    l=lod[8-logh];
    s=32.0f;
    t=256.0f;
    is=INT_TRICK(5);it=INT_TRICK(8);
    ws=4;
    hs=1;
    break;
  case -6:
    aspectratio=GR_ASPECT_1x8;
    l=lod[8-logh];
    s=32.0f;
    t=256.0f;
    is=INT_TRICK(5);it=INT_TRICK(8);
    ws=8;
    hs=1;
    break;
  case -7:
    aspectratio=GR_ASPECT_1x8;
    l=lod[8-logh];
    s=32.0f;
    t=256.0f;
    is=INT_TRICK(5);it=INT_TRICK(8);
    ws=16;
    hs=1;
    break;
  case -8:
    aspectratio=GR_ASPECT_1x8;
    l=lod[8-logh];
    s=32.0f;
    t=256.0f;
    is=INT_TRICK(5);it=INT_TRICK(8);
    ws=32;
    hs=1;
    break;
  default:
    return 0;
    break;
  }

  if(lodlevel)
    (*lodlevel)=l;

  if(ar)
    (*ar)=aspectratio;

  if(sscale)
    (*sscale)=s;

  if(tscale)
    (*tscale)=t;

  if(wscale)
    (*wscale)=ws;

  if(hscale)
    (*hscale)=hs;

  if (i_sscale)
     *i_sscale = is;

  if (i_tscale)
     *i_tscale = it;


  return 1;
}

/*
 * Given an OpenGL internal texture format, return the corresponding
 * Glide internal texture format and base texture format.
 */
void fxTexGetFormat(GLenum glformat, GrTextureFormat_t *tfmt, GLint *ifmt)
{
  switch(glformat) {
    case 1:
    case GL_LUMINANCE:
    case GL_LUMINANCE4:
    case GL_LUMINANCE8:
    case GL_LUMINANCE12:
    case GL_LUMINANCE16:
      if(tfmt)
        (*tfmt)=GR_TEXFMT_INTENSITY_8;
      if(ifmt)
        (*ifmt)=GL_LUMINANCE;
      break;
    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:
      if(tfmt)
        (*tfmt)=GR_TEXFMT_ALPHA_INTENSITY_88;
      if(ifmt)
        (*ifmt)=GL_LUMINANCE_ALPHA;
      break;
    case GL_INTENSITY:
    case GL_INTENSITY4:
    case GL_INTENSITY8:
    case GL_INTENSITY12:
    case GL_INTENSITY16:
      if(tfmt)
        (*tfmt)=GR_TEXFMT_ALPHA_8;
      if(ifmt)
        (*ifmt)=GL_INTENSITY;
      break;
    case GL_ALPHA:
    case GL_ALPHA4:
    case GL_ALPHA8:
    case GL_ALPHA12:
    case GL_ALPHA16:
      if(tfmt)
        (*tfmt)=GR_TEXFMT_ALPHA_8;
      if(ifmt)
        (*ifmt)=GL_ALPHA;
      break;
    case 3:
    case GL_RGB:
    case GL_R3_G3_B2:
    case GL_RGB4:
    case GL_RGB5:
    case GL_RGB8:
    case GL_RGB10:
    case GL_RGB12:
    case GL_RGB16:
      if(tfmt)
        (*tfmt)=GR_TEXFMT_RGB_565;
      if(ifmt)
        (*ifmt)=GL_RGB;
      break;
    case 4:
    case GL_RGBA:
    case GL_RGBA2:
    case GL_RGBA4:
    case GL_RGBA8:
    case GL_RGB10_A2:
    case GL_RGBA12:
    case GL_RGBA16:
      if(tfmt)
        (*tfmt)=GR_TEXFMT_ARGB_4444;
      if(ifmt)
        (*ifmt)=GL_RGBA;
      break;
    case GL_RGB5_A1:
       if(tfmt)
         (*tfmt)=GR_TEXFMT_ARGB_1555;
       if(ifmt)
         (*ifmt)=GL_RGBA;
       break;
    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:
      if(tfmt)
        (*tfmt)=GR_TEXFMT_P_8;
      if(ifmt)
        (*ifmt)=GL_RGBA;   /* XXX why is this RGBA? */
      break;
    default:
      fprintf(stderr,
              "fx Driver: unsupported internalFormat in fxTexGetFormat()\n");
      fxCloseHardware();
      exit(-1);
      break;
  }
}

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

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

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

  return GL_TRUE;
}


/**********************************************************************/
/**** NEW TEXTURE IMAGE FUNCTIONS                                  ****/
/**********************************************************************/

GLboolean fxDDTexImage2D(GLcontext *ctx, GLenum target, GLint level,
                         GLenum format, GLenum type, const GLvoid *pixels,
                         const struct gl_pixelstore_attrib *packing,
                         struct gl_texture_object *texObj,
                         struct gl_texture_image *texImage,
                         GLboolean *retainInternalCopy)
{
  fxMesaContext fxMesa = (fxMesaContext)ctx->DriverCtx;

  if (target != GL_TEXTURE_2D)
    return GL_FALSE;

  if (!texObj->DriverData)
    texObj->DriverData = fxAllocTexObjData(fxMesa);

  if (fxIsTexSupported(target, texImage->IntFormat, texImage)) {
    GrTextureFormat_t gldformat;
    tfxTexInfo *ti = fxTMGetTexInfo(texObj);
    tfxMipMapLevel *mml = &ti->mipmapLevel[level];
    GLint dstWidth, dstHeight, wScale, hScale, texelSize, dstStride;
    GLint mesaFormat;

    fxTexGetFormat(texImage->IntFormat, &gldformat, NULL);

    fxTexGetInfo(texImage->Width, texImage->Height, NULL,NULL,NULL,NULL,
                 NULL,NULL, &wScale, &hScale);
    
    dstWidth = texImage->Width * wScale;
    dstHeight = texImage->Height * hScale;

    switch (texImage->IntFormat) {
      case GL_INTENSITY:
      case GL_INTENSITY4:
      case GL_INTENSITY8:
      case GL_INTENSITY12:
      case GL_INTENSITY16:
        texelSize = 1;
        mesaFormat = MESA_FORMAT_I8;
        break;
      case 1:
      case GL_LUMINANCE:
      case GL_LUMINANCE4:
      case GL_LUMINANCE8:
      case GL_LUMINANCE12:
      case GL_LUMINANCE16:
        texelSize = 1;
        mesaFormat = MESA_FORMAT_L8;
        break;
      case GL_ALPHA:
      case GL_ALPHA4:
      case GL_ALPHA8:
      case GL_ALPHA12:
      case GL_ALPHA16:
        texelSize = 1;
        mesaFormat = MESA_FORMAT_A8;
        break;
      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:
        texelSize = 1;
        mesaFormat = MESA_FORMAT_CI8;
        break;
      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:
        texelSize = 2;
        mesaFormat = MESA_FORMAT_AL88;
        break;
      case 3:
      case GL_RGB:
      case GL_R3_G3_B2:
      case GL_RGB4:
      case GL_RGB5:
      case GL_RGB8:
      case GL_RGB10:
      case GL_RGB12:
      case GL_RGB16:
        texelSize = 2;
        mesaFormat = MESA_FORMAT_RGB565;
        break;
      case 4:
      case GL_RGBA:
      case GL_RGBA2:
      case GL_RGBA4:
      case GL_RGBA8:
      case GL_RGB10_A2:
      case GL_RGBA12:
      case GL_RGBA16:
        texelSize = 2;
        mesaFormat = MESA_FORMAT_ARGB4444;
        break;
      case GL_RGB5_A1:
        texelSize = 2;
        mesaFormat = MESA_FORMAT_ARGB1555;
        break;
      default:
        gl_problem(NULL, "tdfx driver: texbuildimagemap() bad format");
        return GL_FALSE;
    }

    /*
    _mesa_set_teximage_component_sizes(mesaFormat, texImage);
    */

    /*printf("teximage:\n");*/
    /* allocate new storage for texture image, if needed */
    if (!mml->data || mml->glideFormat != gldformat ||
        mml->width != dstWidth || mml->height != dstHeight) {
      if (mml->data)
        FREE(mml->data);
      mml->data = MALLOC(dstWidth * dstHeight * texelSize);
      if (!mml->data)
        return GL_FALSE;
      mml->glideFormat = gldformat;
      mml->width = dstWidth;
      mml->height = dstHeight;
      fxTexInvalidate(ctx, texObj);
    }

    dstStride = dstWidth * texelSize;

    /* store the texture image */
    if (!_mesa_convert_teximage(mesaFormat, dstWidth, dstHeight, mml->data,
                                dstStride,
                                texImage->Width, texImage->Height,
                                format, type, pixels, packing)) {
      return GL_FALSE;
    }
    
    if (ti->validated && ti->isInTM) {
      /*printf("reloadmipmaplevels\n");*/
      fxTMReloadMipMapLevel(fxMesa, texObj, level);
    }
    else {
      /*printf("invalidate2\n");*/
      fxTexInvalidate(ctx,texObj);
    }

    *retainInternalCopy = GL_FALSE;
    return GL_TRUE;
  }
  else {
    gl_problem(NULL, "fx Driver: unsupported texture in fxDDTexImg()\n");
    return GL_FALSE;
  }
}


GLboolean 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 = (fxMesaContext) ctx->DriverCtx;
  tfxTexInfo *ti;
  GLint wscale, hscale, dstStride;
  tfxMipMapLevel *mml;
  GLboolean result;

  if (target != GL_TEXTURE_2D)
    return GL_FALSE;

  if (!texObj->DriverData)
    return GL_FALSE;

  ti = fxTMGetTexInfo(texObj);
  mml = &ti->mipmapLevel[level];

  fxTexGetInfo( texImage->Width, texImage->Height, NULL,NULL,NULL,NULL,
                NULL,NULL, &wscale, &hscale);

  assert(mml->data);  /* must have an existing texture image! */

  switch (mml->glideFormat) {
    case GR_TEXFMT_INTENSITY_8:
      dstStride = mml->width;
      result = _mesa_convert_texsubimage(MESA_FORMAT_CI8, xoffset, yoffset,
                                         mml->width, mml->height, mml->data,
                                         dstStride, width, height,
                                         texImage->Width, texImage->Height,
                                         format, type, pixels, packing);
      break;
    case GR_TEXFMT_ALPHA_8:
      dstStride = mml->width;
      result = _mesa_convert_texsubimage(MESA_FORMAT_A8, xoffset, yoffset,
                                         mml->width, mml->height, mml->data,
                                         dstStride, width, height,
                                         texImage->Width, texImage->Height,
                                         format, type, pixels, packing);
      break;
    case GR_TEXFMT_P_8:
      dstStride = mml->width;
      result = _mesa_convert_texsubimage(MESA_FORMAT_CI8, xoffset, yoffset,
                                         mml->width, mml->height, mml->data,
                                         dstStride, width, height,
                                         texImage->Width, texImage->Height,
                                         format, type, pixels, packing);
      break;
    case GR_TEXFMT_ALPHA_INTENSITY_88:
      dstStride = mml->width * 2;
      result = _mesa_convert_texsubimage(MESA_FORMAT_AL88, xoffset, yoffset,
                                         mml->width, mml->height, mml->data,
                                         dstStride, width, height,
                                         texImage->Width, texImage->Height,
                                         format, type, pixels, packing);
      break;
    case GR_TEXFMT_RGB_565:
      dstStride = mml->width * 2;
      result = _mesa_convert_texsubimage(MESA_FORMAT_RGB565, xoffset, yoffset,
                                         mml->width, mml->height, mml->data,
                                         dstStride, width, height,
                                         texImage->Width, texImage->Height,
                                         format, type, pixels, packing);
      break;
    case GR_TEXFMT_ARGB_4444:
      dstStride = mml->width * 2;
      result = _mesa_convert_texsubimage(MESA_FORMAT_ARGB4444, xoffset, yoffset,
                                         mml->width, mml->height, mml->data,
                                         dstStride, width, height,
                                         texImage->Width, texImage->Height,
                                         format, type, pixels, packing);
      break;
    case GR_TEXFMT_ARGB_1555:
      dstStride = mml->width * 2;
      result = _mesa_convert_texsubimage(MESA_FORMAT_ARGB1555, xoffset, yoffset,
                                         mml->width, mml->height, mml->data,
                                         dstStride, width, height,
                                         texImage->Width, texImage->Height,
                                         format, type, pixels, packing);
      break;
    default:
      gl_problem(NULL, "tdfx driver: fxTexBuildSubImageMap() bad format");
      result = GL_FALSE;
  }

  if (!result) {
    return GL_FALSE;
  }

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

  return GL_TRUE;
}


#if 0
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)
        printf("%02x %02x  ", data[0], data[1]);
      else if (c==3)
        printf("%02x %02x %02x  ", data[0], data[1], data[2]);
      data += c;
    }
    printf("\n");
  }
}
#endif


GLvoid *fxDDGetTexImage(GLcontext *ctx, GLenum target, GLint level,
                        const struct gl_texture_object *texObj,
                        GLenum *formatOut, GLenum *typeOut,
                        GLboolean *freeImageOut )
{
  tfxTexInfo *ti;
  tfxMipMapLevel *mml;

  if (target != GL_TEXTURE_2D)
    return NULL;

  if (!texObj->DriverData)
    return NULL;

  ti = fxTMGetTexInfo(texObj);
  mml = &ti->mipmapLevel[level];
  if (mml->data) {
    GLint mesaFormat;
    GLenum glFormat;
    struct gl_texture_image *texImage = texObj->Image[level];
    GLint srcStride;

    GLubyte *data = (GLubyte *) MALLOC(texImage->Width * texImage->Height * 4);
    if (!data)
      return NULL;

    switch (mml->glideFormat) {
      case GR_TEXFMT_INTENSITY_8:
        mesaFormat = MESA_FORMAT_CI8;
        glFormat = GL_INTENSITY;
        srcStride = mml->width;
        break;
      case GR_TEXFMT_ALPHA_INTENSITY_88:
        mesaFormat = MESA_FORMAT_AL88;
        glFormat = GL_LUMINANCE_ALPHA;
        srcStride = mml->width;
        break;
      case GR_TEXFMT_ALPHA_8:
        mesaFormat = MESA_FORMAT_A8;
        glFormat = GL_ALPHA;
        srcStride = mml->width;
        break;
      case GR_TEXFMT_RGB_565:
        mesaFormat = MESA_FORMAT_RGB565;
        glFormat = GL_RGB;
        srcStride = mml->width * 2;
        break;
      case GR_TEXFMT_ARGB_4444:
        mesaFormat = MESA_FORMAT_ARGB4444;
        glFormat = GL_RGBA;
        srcStride = mml->width * 2;
        break;
      case GR_TEXFMT_ARGB_1555:
        mesaFormat = MESA_FORMAT_ARGB1555;
        glFormat = GL_RGBA;
        srcStride = mml->width * 2;
        break;
      case GR_TEXFMT_P_8:
        mesaFormat = MESA_FORMAT_CI8;
        glFormat = GL_COLOR_INDEX;
        srcStride = mml->width;
        break;
      default:
        gl_problem(NULL, "Bad glideFormat in fxDDGetTexImage");
        return NULL;
    }
    _mesa_unconvert_teximage(mesaFormat, mml->width, mml->height, mml->data,
                             srcStride, texImage->Width, texImage->Height,
                             glFormat, data);
    *formatOut = glFormat;
    *typeOut = GL_UNSIGNED_BYTE;
    *freeImageOut = GL_TRUE;
    return data;
  }
  else {
    return NULL;
  }
}


#else


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

int gl_fx_dummy_function_ddtex(void)
{
  return 0;
}

#endif  /* FX */
