/* GLP library implementation v0.1 by Antoine Mine 4/07/98

   Find any help in glp.html.
 */

#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "glp.h"

static GLfloat* tex = NULL;
static GLuint tex2Dl,tex2Drgb;
static GLuint tex3Dl,tex3Drgb;
static GLuint texId;

static int mode3D = 0;
static int modeRgb = 1;

static GLuint  octave = 4;
static GLfloat freq_mult = 2.0;
static GLfloat amp_div = 2.0;
static GLfloat amplitude = 1.0;
static GLuint max = 16;
static GLboolean noisedirty = GL_TRUE;
static GLfloat noise_amp = 0.5;
static GLfloat noise_begin = 0.0;
static GLfloat noise_end = 1.0;

static GLuint maxfun = 256;
static GLenum funmode = GLP_MODE_RAMP;
static GLfloat fun_amp = 0.5;
static GLboolean iddirty = GL_TRUE;
static GLfloat* id = NULL;

static GLuint mapcoord;

static GLenum error = GLP_NO_ERROR;
static const GLubyte* error_msg[] =
{ "out of memory", "bad parameter", "bad value",
  "harward capacity exceded",
  "not supported by harward" };

static GLfloat*  colormap = NULL;
static GLfloat*  gcolormap = NULL;
static GLboolean colormapdirty = GL_TRUE;
static GLfloat   colormap_gamma = 1.0;

static GLboolean texture_ext;
static GLboolean texture_object_ext;
static GLboolean texture_color_table_ext;
static GLboolean pixel_texture_ext;
static GLboolean texture_3D_ext;
static GLboolean color_matrix_ext;
static GLboolean color_table_ext;
static GLboolean blend_ext;


static GLint vp[4];

/* Some actions are delayed in order to optimize successives procedure calls
 
   pass says where last action has been interrupted
   call glpFinalize to compleet interupted action
 */

#define PASS_NOTHING         0
#define PASS_DRAW_PATTERN    1
#define PASS_DRAW_NOISE      2
static int pass = PASS_NOTHING;




/* Error Gestion
 */
  
GLenum glpGetError() {
  int e = error;
  error = GLP_NO_ERROR;
  return (e==GLP_NO_ERROR)?glGetError():e;
}

const GLubyte* glpErrorString(GLenum error) {
  if (error>=GLP_FIRST_ERROR_CODE && error<GLP_LAST_ERROR_CODE)
    return error_msg[error-GLP_FIRST_ERROR_CODE];
  return gluErrorString(error);
}


static void seterror(int err) {
  error = (error==GLP_NO_ERROR)?err:error;
}

static int ispowerof2(GLuint i) {
  if (!i) return 0;
  while (!(i&1)) i>>=1;
  if (i==1) return 1;
  return 0;
}

/* Extension support verification
 */

GLboolean glpQueryExtension(const char *extName)
{
  char *p;
  char *end;
  int extNameLen;  
  
  extNameLen = strlen(extName);
  
  p = (char *)glGetString(GL_EXTENSIONS);
  if (NULL == p) {
    return GL_FALSE;
  }
  
  end = p + strlen(p);  
  
  while (p < end) {
    int n = strcspn(p, " ");
    if ((extNameLen == n) && (strncmp(extName, p, n) == 0)) {
      return GL_TRUE;
    }
    p += (n + 1);
  }
  return GL_FALSE;
}




/* Initialisation
 */

void glpInit() {
  float s[4] = { 1.0, 0.0, 0.0, 0.0};
  float t[4] = { 0.0, 1.0, 0.0, 0.0};
  float r[4] = { 0.0, 0.0, 1.0, 0.0};
  
  printf("GLP initialisation...\n\n");
  
  /* Verify extensions
   */

  texture_ext =  glpQueryExtension("GL_EXT_texture");
  texture_object_ext = glpQueryExtension("GL_EXT_texture_object");
  texture_3D_ext = glpQueryExtension("GL_EXT_texture3D");
  color_matrix_ext = glpQueryExtension("GL_SGI_color_matrix");
  color_table_ext = glpQueryExtension("GL_SGI_color_table");
  blend_ext = glpQueryExtension("GL_EXT_blend_color") &
    glpQueryExtension("GL_EXT_blend_subtract");
  texture_color_table_ext = glpQueryExtension("GL_SGI_texture_color_table");
  pixel_texture_ext = glpQueryExtension("GL_SGIX_pixel_texture");

  printf("Color matrix extension          : %s\n",color_matrix_ext?"Yes":"No");
  printf("Color table extension           : %s\n",color_table_ext?"Yes":"No");
  printf("Blend extensions                : %s\n",blend_ext?"Yes":"No");
  printf("Texture extension               : %s\n",texture_ext?"Yes":"No");
  printf("Texture object extension        : %s\n",texture_object_ext?"Yes":"No");
  printf("Texture 3D extension            : %s\n",texture_3D_ext?"Yes":"no");
  printf("Texture color table extension   : %s\n",texture_color_table_ext?"yes":"No");
  printf("Pixel texture extension         : %s\n",pixel_texture_ext?"Yes":"No");

  /* Init texture coord lists
   */

  mapcoord = glGenLists(1);
  glNewList(mapcoord, GL_COMPILE);
  glEnable(GL_TEXTURE_GEN_R);
  glEnable(GL_TEXTURE_GEN_S);
  glEnable(GL_TEXTURE_GEN_T);
  glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
  glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
  glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
  glTexGenfv(GL_S,GL_OBJECT_PLANE,s);
  glTexGenfv(GL_T,GL_OBJECT_PLANE,t);
  glTexGenfv(GL_R,GL_OBJECT_PLANE,r);
  glEndList();

  printf("\nGLP initialisation completed\n\n");
}

static long seed = 0x12345678;

static void buildnoise() {
  int i,j,k,l;
  GLint dummy;
  
  /* Init random noise table
   */

  srand48(seed);
  
  if (tex) {
    free(tex);
    glDeleteTexturesEXT(1,&tex3Drgb);
    glDeleteTexturesEXT(1,&tex3Dl);
    glDeleteTexturesEXT(1,&tex2Drgb);
    glDeleteTexturesEXT(1,&tex2Dl);
  }

    
  tex = (GLfloat*) malloc(max*max*max*3*sizeof(GLfloat));
  if (!tex) { seterror(GLP_OUT_OF_MEMORY); return; }

  for (i=0;i<max;i++)
    for (j=0;j<max;j++)
      for (k=0;k<max;k++)
        for (l=0;l<3;l++)
          tex[((i*max+j)*max+k)*3+l] = drand48();


  /* Init noise texture objects
   */

  glPixelStorei(GL_UNPACK_ALIGNMENT,1);

  if (!texture_object_ext) seterror(GLP_NOT_SUPPORTED);
  if (!texture_ext) seterror(GLP_NOT_SUPPORTED);

  glGenTexturesEXT(1,&tex3Drgb);
  glBindTextureEXT(GL_TEXTURE_3D_EXT,tex3Drgb);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_S    , GL_REPEAT);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_T    , GL_REPEAT);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_R_EXT, GL_REPEAT);
  glTexImage3DEXT(GL_PROXY_TEXTURE_3D_EXT,0,GL_RGB,
                  max,max,max, 0, GL_RGB, GL_FLOAT, NULL);
  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D_EXT,0,GL_TEXTURE_WIDTH,&dummy);
  if (!dummy) seterror(GLP_HARD);
  glTexImage3DEXT(GL_TEXTURE_3D_EXT,0,GL_RGB,
                  max,max,max, 0, GL_RGB, GL_FLOAT, tex);

  glGenTexturesEXT(1,&tex3Dl);
  glBindTextureEXT(GL_TEXTURE_3D_EXT,tex3Dl);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_S    , GL_REPEAT);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_T    , GL_REPEAT);
  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_R_EXT, GL_REPEAT);
  glTexImage3DEXT(GL_PROXY_TEXTURE_3D_EXT,0,GL_INTENSITY_EXT,
                  max,max,max, 0, GL_LUMINANCE, GL_FLOAT, NULL);
  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D_EXT,0,GL_TEXTURE_WIDTH,&dummy);
  if (!dummy) seterror(GLP_HARD);
  glTexImage3DEXT(GL_TEXTURE_3D_EXT,0,GL_INTENSITY_EXT,
                  max,max,max, 0, GL_LUMINANCE, GL_FLOAT, tex);

  glGenTexturesEXT(1,&tex2Drgb);
  glBindTextureEXT(GL_TEXTURE_2D,tex2Drgb);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S    , GL_REPEAT); 
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T    , GL_REPEAT);
  glTexImage2D(GL_PROXY_TEXTURE_2D_EXT,0,GL_RGB,
               max,max, 0, GL_RGB, GL_FLOAT, NULL);
  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D_EXT,0,GL_TEXTURE_WIDTH,&dummy);
  if (!dummy) seterror(GLP_HARD);
  glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,
               max,max, 0, GL_RGB, GL_FLOAT, tex);

  glGenTexturesEXT(1,&tex2Dl);
  glBindTextureEXT(GL_TEXTURE_2D,tex2Dl);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S    , GL_REPEAT); 
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T    , GL_REPEAT);
  glTexImage2D(GL_PROXY_TEXTURE_2D_EXT,0,GL_INTENSITY_EXT,
               max,max, 0, GL_LUMINANCE, GL_FLOAT, NULL);
  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D_EXT,0,GL_TEXTURE_WIDTH,&dummy);
  if (!dummy) seterror(GLP_HARD);
  glTexImage2D(GL_TEXTURE_2D,0,GL_INTENSITY_EXT,
               max,max, 0, GL_LUMINANCE, GL_FLOAT, tex);

  noisedirty = GL_FALSE;
}



static void buildid() {
  GLint dummy;
  int i;
  
  if (id) {
    free(id);
    glDeleteTexturesEXT(1,&texId);
  }
  id = (GLfloat*) malloc(sizeof(GLfloat)*maxfun);
  if (!id) { seterror(GLP_OUT_OF_MEMORY); return; }

  switch (funmode) {

  case GLP_MODE_TRIANGULAR:
    for (i=0;i<maxfun/2;i++) {
      id[i] = 2.0/maxfun*i;
      id[i+maxfun/2] = 1.0-2.0/maxfun*i;
    }
    break;

  case GLP_MODE_RAMP:
    for (i=0;i<maxfun;i++)
      id[i] = 1.0/(maxfun-1)*i;
    break;

  case GLP_MODE_SINUSOIDAL:
    for (i=0;i<maxfun;i++)
      id[i] = (cos(2.0*M_PI*i/maxfun+M_PI)+1.0)/2.0;
    break;
  }
  
  glGenTexturesEXT(1,&texId);
  glBindTextureEXT(GL_TEXTURE_1D,texId);
  if (funmode==GLP_MODE_RAMP) {
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  }
  else {
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  }
  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S    , GL_REPEAT);
  glTexImage1D(GL_PROXY_TEXTURE_1D_EXT,0,GL_INTENSITY_EXT,
               maxfun, 0, GL_LUMINANCE, GL_FLOAT, NULL);
  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D_EXT,0,GL_TEXTURE_WIDTH,&dummy);
  if (!dummy) seterror(GLP_HARD);
  glTexImage1D(GL_TEXTURE_1D,0,GL_INTENSITY_EXT,
               maxfun, 0, GL_LUMINANCE, GL_FLOAT, id);
  
  iddirty = GL_FALSE;
}

static void buildcolormap();

void glpFlush() {

  if (iddirty) buildid();
  if (noisedirty) buildnoise();
  if (colormapdirty) buildcolormap();
}


/* Set/Get parameters
 */

void glpSetParameteri(GLenum param,GLuint value) {
  switch (param) {

  case GLP_DIMENSION :
    switch (value) {
    case GLP_MODE_2D : mode3D = 0; break;
    case GLP_MODE_3D : mode3D = 1;
      if (!texture_3D_ext) { seterror(GLP_NOT_SUPPORTED); mode3D = 0; }
      break;
    default : seterror(GLP_BAD_VALUE);
    }
    break;
    
  case GLP_COMPONENT :
    switch (value) {
    case GLP_MODE_RGB : modeRgb = 1; break;
    case GLP_MODE_L   : modeRgb = 0; break;
    default : seterror(GLP_BAD_VALUE);
    }
    break;

  case GLP_FUN_MODE :
    switch (value) {
    case GLP_MODE_RAMP :
    case GLP_MODE_TRIANGULAR :
    case GLP_MODE_SINUSOIDAL :
      funmode = value;
      iddirty = GL_TRUE;
      break;
    default : seterror(GLP_BAD_VALUE);
    }
    break;

    
  case GLP_OCTAVE : octave = value; break;

  case GLP_NOISE_SIZE :
    if (!ispowerof2(value)) { seterror(GLP_BAD_VALUE); return; }
    max = value; noisedirty = GL_TRUE; break;

  case GLP_FUN_SIZE :
    if (!ispowerof2(value)) { seterror(GLP_BAD_VALUE); return; }
    maxfun = value; iddirty = GL_TRUE; break;
    
  case GLP_FREQ_MULT :
  case GLP_AMP_DIV :
  case GLP_NOISE_AMP :
  case GLP_FUN_AMP :
  case GLP_NOISE_BEGIN:
  case GLP_NOISE_END:
  case GLP_GAMMA:
    glpSetParameterf(param,value);
    break;
    
  default : seterror(GLP_BAD_PARAMETER);
  }
}


void glpSetParameterf(GLenum param,GLfloat value) {
  switch (param) {

  case GLP_FREQ_MULT :
    if (value<=0.0) { seterror(GLP_BAD_VALUE); return; }
    freq_mult = value; break;
  case GLP_AMP_DIV :
    if (value<=0.0) { seterror(GLP_BAD_VALUE); return; }
    amp_div = value; break;
  case GLP_FUN_AMP :
    if (value<0.0 || value>1.0) { seterror(GLP_BAD_VALUE); return; }
    fun_amp = value; break;
  case GLP_NOISE_AMP :
    if (value<0.0 || value>1.0) { seterror(GLP_BAD_VALUE); return; }
    noise_amp = value; break;
  case GLP_NOISE_BEGIN :
    if (value<0.0 || value>1.0) { seterror(GLP_BAD_VALUE); return; }
    noise_begin = value; break;
  case GLP_NOISE_END :
    if (value<0.0 || value>1.0) { seterror(GLP_BAD_VALUE); return; }
    noise_end = value; break;

  case GLP_GAMMA :
    if (value<=0.0) { seterror(GLP_BAD_VALUE); return; }
    colormap_gamma = value;
    colormapdirty = GL_TRUE; break;

  case GLP_FUN_MODE:
  case GLP_FUN_SIZE:
  case GLP_NOISE_SIZE:
  case GLP_DIMENSION:
  case GLP_COMPONENT:
  case GLP_OCTAVE:
      glpSetParameteri(param,value);
      break;
    
  default : seterror(GLP_BAD_PARAMETER);
  }
}



void glpGetParameteri(GLenum param,GLuint* value) {
  switch (param) {

  case GLP_DIMENSION:
    *value = mode3D?GLP_MODE_3D:GLP_MODE_2D;
    break;

  case GLP_COMPONENT:
    *value = modeRgb?GLP_MODE_RGB:GLP_MODE_L;
    break;
    
  case GLP_OCTAVE:
    *value = octave;
    break;

  case GLP_FUN_MODE:
    *value = funmode;
    break;

  case GLP_FUN_SIZE:
    *value = maxfun;
    break;

  case GLP_NOISE_SIZE:
    *value = max;
    break;
    
  case GLP_FREQ_MULT :
  case GLP_AMP_DIV :
  case GLP_NOISE_AMP:
  case GLP_FUN_AMP:
  case GLP_NOISE_BEGIN:
  case GLP_NOISE_END:
  case GLP_GAMMA:
    seterror(GLP_BAD_VALUE);
    break;
    
  default : seterror(GLP_BAD_PARAMETER);
  }
}


void glpGetParameterf(GLenum param,GLfloat* value) {
  switch (param) {

  case GLP_FREQ_MULT :
    *value = freq_mult;
    break;
  case GLP_AMP_DIV :
    *value = amp_div;
    break;
  case GLP_NOISE_AMP :
    *value = noise_amp;
    break;
  case GLP_FUN_AMP :
    *value = fun_amp;
    break;
  case GLP_NOISE_BEGIN :
    *value = noise_begin;
    break;
  case GLP_NOISE_END :
    *value = noise_end;
    break;
  case GLP_GAMMA :
    *value = colormap_gamma;
    break;

  case GLP_DIMENSION:
  case GLP_COMPONENT:
  case GLP_OCTAVE:
  case GLP_FUN_MODE:
  case GLP_FUN_SIZE:
  case GLP_NOISE_SIZE:
    seterror(GLP_BAD_VALUE);
    break;
    
  default : seterror(GLP_BAD_PARAMETER);
  }
}



/* Draw noise

   changed state :
      texture changed & disabled
      texture coord generation changed & disabled
      texture environement function is changed
      blending changed & disabled
      depth mask set to GL_FALSE (function not changed)
      polygon offset disabled (not changed)
      
      */

static GLfloat qpow(GLfloat q,int p) {
  GLfloat f = 1.0;
  for (;p;p>>=1,q*=q)
    if (p&1) f*=q;
  return f;
}

void glpDrawNoise(GLuint list) {
  int i;
  GLfloat f,a;
  
  if (!octave) return;

  glpFinalize();

  if (noisedirty) buildnoise();

  glCallList(mapcoord);
  
  if (mode3D) {
    glEnable(GL_TEXTURE_3D_EXT);
    glBindTextureEXT(GL_TEXTURE_3D_EXT,modeRgb?tex3Drgb:tex3Dl);
  }
  else {
    glEnable(GL_TEXTURE_2D);
    glBindTextureEXT(GL_TEXTURE_2D,modeRgb?tex2Drgb:tex2Dl);
  }

  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
  
  glMatrixMode(GL_TEXTURE);
  glPushMatrix();
  f = 1.0;
  a = noise_amp;
  for (i=0;i<octave;i++,f*=freq_mult,a/=amp_div) {
    glScalef(f,f,f);
    glColor3f(a,a,a);
    glCallList(list);
    glMatrixMode(GL_TEXTURE);
    glPopMatrix();
    glPushMatrix();
    if (!i) {
      if (!blend_ext) { seterror(GLP_NOT_SUPPORTED); return; }
      glDepthMask(GL_FALSE);
      glDisable(GL_POLYGON_OFFSET_EXT);
      glBlendFunc(GL_ONE,GL_ONE);
      glEnable(GL_BLEND);
      glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
    }
  }
  glPopMatrix();
  
  glDisable(GL_BLEND);
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  glDisable(GL_TEXTURE_GEN_R);
  if (mode3D)
    glDisable(GL_TEXTURE_3D_EXT);
  else
    glDisable(GL_TEXTURE_2D);

  if ((noise_begin>0.0 || noise_end<1.0) && noise_begin!=noise_end) {
    glPushAttrib(GL_PIXEL_MODE_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    glPixelTransferf(GL_RED_SCALE  ,1.0/(noise_end-noise_begin));
    glPixelTransferf(GL_GREEN_SCALE,1.0/(noise_end-noise_begin));
    glPixelTransferf(GL_BLUE_SCALE ,1.0/(noise_end-noise_begin));
    glPixelTransferf(GL_RED_BIAS   ,-noise_begin/(noise_end-noise_begin));
    glPixelTransferf(GL_GREEN_BIAS ,-noise_begin/(noise_end-noise_begin));
    glPixelTransferf(GL_BLUE_BIAS  ,-noise_begin/(noise_end-noise_begin));
    
    glGetIntegerv(GL_VIEWPORT,vp);
    glDisable(GL_DEPTH_TEST);
    glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    /* Must be continued... in glpDoColormap or glpFinalize */
    pass = PASS_DRAW_NOISE;
 }
}


/* Colormap
 */

static GLuint cmap_sizetab = 0;

void glpSetColormap(GLuint sizetab,GLuint nbtab,GLenum mode,
                    GLuint size,float* tab) {
  
  int i,j,k;
  int minitab = sizetab/nbtab+1;
  
  if (mode!=GLP_NORM) { seterror(GLP_BAD_PARAMETER); return; }

  if (!ispowerof2(sizetab) || !nbtab || nbtab>sizetab) {
    seterror(GLP_BAD_PARAMETER);
    return;
  }
  
  if (colormap) free(colormap);
  colormap = (GLfloat*) malloc(sizeof(GLfloat)*4*minitab*nbtab);
  if (!colormap) { seterror(GLP_OUT_OF_MEMORY); return; }
  
  if (size) {
    for (j=0;j<minitab*tab[0];j++) {
      colormap[j*4+0] = tab[1];
      colormap[j*4+1] = tab[2];
      colormap[j*4+2] = tab[3];
      colormap[j*4+3] = tab[4];
    }
    for (j=minitab*tab[(size-1)*5];j<minitab;j++) {
      colormap[j*4+0] = tab[(size-1)*5+1];
      colormap[j*4+1] = tab[(size-1)*5+2];
      colormap[j*4+2] = tab[(size-1)*5+3];
      colormap[j*4+3] = tab[(size-1)*5+4];
    }
  }
  else
    for (j=0;j<minitab*4;j++)
      colormap[j] = 1.00;

  if (size)
    for (i=0;i<size-1;i++) {
      float r,g,b,a;
      float dr,dg,db,da;
      r = tab[i*5+1];
      g = tab[i*5+2];
      b = tab[i*5+3];
      a = tab[i*5+4];
      dr = (tab[(i+1)*5+1]-tab[i*5+1])/(tab[(i+1)*5]-tab[i*5])/minitab;
      dg = (tab[(i+1)*5+2]-tab[i*5+2])/(tab[(i+1)*5]-tab[i*5])/minitab;
      db = (tab[(i+1)*5+3]-tab[i*5+3])/(tab[(i+1)*5]-tab[i*5])/minitab;
      da = (tab[(i+1)*5+4]-tab[i*5+4])/(tab[(i+1)*5]-tab[i*5])/minitab;
      for (j=minitab*tab[i*5];j<minitab*tab[(i+1)*5];
           j++,r+=dr,g+=dg,b+=db,a+=da) {
        colormap[j*4+0] = r;
        colormap[j*4+1] = g;
        colormap[j*4+2] = b;
        colormap[j*4+3] = a;
      }
    }
  
  for (i=1;i<nbtab;i++)
    memcpy(colormap+((int)((float)i*sizetab/nbtab))*4,colormap,sizeof(GLfloat)*4*minitab);

  cmap_sizetab = sizetab;
  colormapdirty = GL_TRUE;
}


void glpResetColormap() {

  if (!color_table_ext) { seterror(GLP_NOT_SUPPORTED); return; }
  if (colormapdirty) { buildcolormap(); return; }

  if (colormap_gamma==1.0)
    if (!colormap) return;
    else glColorTableSGI(GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI,GL_RGBA,
                         cmap_sizetab,GL_RGBA,GL_FLOAT,colormap);
  else
    if (!gcolormap) return;
    else glColorTableSGI(GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI,GL_RGBA,
                         cmap_sizetab,GL_RGBA,GL_FLOAT,gcolormap);
}

static void buildcolormap() {

  if (gcolormap) free(gcolormap);
  gcolormap = NULL;

  colormapdirty = GL_FALSE;
  if (!colormap) return;
  
  if (colormap_gamma!=1.0) {
    int i;
    
    gcolormap = (GLfloat*) malloc(sizeof(GLfloat)*4*cmap_sizetab);
    if (!gcolormap) { seterror(GLP_OUT_OF_MEMORY); return; }
    
    for (i=0;i<cmap_sizetab;i++) {
      int j = pow((float)i/cmap_sizetab,colormap_gamma)*cmap_sizetab;
      gcolormap[i*4+0] = colormap[j*4+0];
      gcolormap[i*4+1] = colormap[j*4+1];
      gcolormap[i*4+2] = colormap[j*4+2];
      gcolormap[i*4+3] = colormap[j*4+3];
    }
  } 
 
  glpResetColormap();
}



/* Applys colormap

   state changed :
     post_color_matrix_color_table disabled (table not changed,
     should have been set by glpSetColormap or glpResetColormap)
   
 */

void glpDoColormap() {

  if (colormapdirty) buildcolormap();

  if (pass!=PASS_DRAW_PATTERN && pass!=PASS_DRAW_NOISE) {
    glpFinalize();
    glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glDisable(GL_DEPTH_TEST);
    glGetIntegerv(GL_VIEWPORT,vp);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
  }
  
  glRasterPos2f(-1,-1);
  glEnable(GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI);
  glCopyPixels(0,0,vp[2],vp[3],GL_COLOR);
  glDisable(GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI);
  glPopAttrib();
  
  switch (pass) {

  case PASS_DRAW_NOISE:
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    pass = PASS_NOTHING;
    break;

  case PASS_DRAW_PATTERN:
    glMatrixMode(GL_COLOR);
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    pass = PASS_NOTHING;
    break;
    
  default:
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
  }
}







/*  Draw pattern, passes may be delayed

    state changed (valid only after a glpFinalize) :
      texture changed & disabled
      texture coord generation changed & disabled
      blending changed & disabled
      polygon offset disabled (not changed)
      color mask set to all GL_TRUE
      
 */


void glpDrawPattern(GLuint scene,GLenum func) {

  glpFinalize();

  glCallList(mapcoord);
  if (iddirty) buildid();

  glEnable(GL_TEXTURE_1D);
  glBindTextureEXT(GL_TEXTURE_1D,texId);

  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
  glColor3f(fun_amp,fun_amp,fun_amp);
    
  /* First pass in 3D
   */
  
  switch (func) {

  case GLP_FUNC_GRADIENT:
    glCallList(scene);

    glDepthMask(GL_FALSE);
    glDisable(GL_POLYGON_OFFSET_EXT);
    break;
    
  case GLP_FUNC_CYLINDER:
    glColorMask(GL_TRUE,GL_FALSE,GL_FALSE,GL_FALSE);
    glCallList(scene);

    glDepthMask(GL_FALSE);
    glDisable(GL_POLYGON_OFFSET_EXT);
    
    glColorMask(GL_FALSE,GL_TRUE,GL_FALSE,GL_FALSE);
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glRotatef(90.0,0.0,0.0,1.0);
    glCallList(scene);
    glMatrixMode(GL_TEXTURE);
    glPopMatrix();

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    break;

  case GLP_FUNC_SPHERE:
    glColorMask(GL_TRUE,GL_FALSE,GL_FALSE,GL_FALSE);
    glCallList(scene);

    glDepthMask(GL_FALSE);
    glDisable(GL_POLYGON_OFFSET_EXT);

    glColorMask(GL_FALSE,GL_TRUE,GL_FALSE,GL_FALSE);
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glRotatef(90.0,0.0,0.0,1.0);
    glCallList(scene);
    glMatrixMode(GL_TEXTURE);
    glPopMatrix();

    glColorMask(GL_FALSE,GL_FALSE,GL_TRUE,GL_FALSE);
    glPushMatrix();
    glRotatef(90.0,0.0,1.0,0.0);
    glCallList(scene);
    glMatrixMode(GL_TEXTURE);
    glPopMatrix();

    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    break;

  default:
    seterror(GLP_BAD_VALUE);
    break;

  }
  glDisable(GL_TEXTURE_1D);
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  glDisable(GL_TEXTURE_GEN_R);

  if (func==GLP_FUNC_GRADIENT) {
    glDisable(GL_BLEND);
    return;
  }
  
  /* Second pass in 2D
   */
  
  glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  glGetIntegerv(GL_VIEWPORT,vp);
  glDisable(GL_DEPTH_TEST);
  glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  switch (func) {
    
  case GLP_FUNC_CYLINDER: {
    GLfloat mat[16] = {
      0.5, 0.5, 0.5, 0.5,
      0.5, 0.5, 0.5, 0.5,
      0.0, 0.0, 0.0, 0.0,
      0.0, 0.0, 0.0, 0.0 };
    
    if (!color_matrix_ext) seterror(GLP_NOT_SUPPORTED);

    glRasterPos2f(-1,-1);
    glEnable(GL_BLEND);
    glBlendFunc(GL_ZERO,GL_SRC_COLOR);
    glReadBuffer(GL_BACK);
    glCopyPixels(0,0,vp[2],vp[3],GL_COLOR);
    glDisable(GL_BLEND);
    
    glMatrixMode(GL_COLOR);
    glPushMatrix();
    glLoadMatrixf(mat);
    
    /* Must be continued... in glpDoColormap or glpFinalize */
    pass = PASS_DRAW_PATTERN;
  }
  break;

  case GLP_FUNC_SPHERE: {
    GLfloat mat[16] = {
      0.33, 0.33, 0.33, 0.33,
      0.33, 0.33, 0.33, 0.33,
      0.33, 0.33, 0.33, 0.33,
      0.00, 0.00, 0.00, 0.00 };
    
    if (!color_matrix_ext) seterror(GLP_NOT_SUPPORTED);
    glRasterPos2f(-1,-1);
    glEnable(GL_BLEND);
    glBlendFunc(GL_DST_COLOR,GL_NONE);
    glCopyPixels(0,0,vp[2],vp[3],GL_COLOR);
    glDisable(GL_BLEND);
    
    glMatrixMode(GL_COLOR);
    glPushMatrix();
    glLoadMatrixf(mat);
    
    /* Must be continued... in glpDoColormap or glpFinalize */
    pass = PASS_DRAW_PATTERN;
  }
  break;
  
  default:
    seterror(GLP_BAD_VALUE);
    break;
    
  }
}



/* Flush any begun pass
 */

void glpFinalize() {

  switch (pass) {

  case PASS_DRAW_PATTERN:
    glRasterPos2f(-1,-1);
    glCopyPixels(0,0,vp[2],vp[3],GL_COLOR);
    glMatrixMode(GL_COLOR);
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glPopAttrib();
    break;

  case PASS_DRAW_NOISE:
    glRasterPos2f(-1,-1);
    glCopyPixels(0,0,vp[2],vp[3],GL_COLOR);
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glPopAttrib();
    break;
    
  }

  pass = PASS_NOTHING;
}