/* interp.c */

/*
 * Mesa 3-D graphics library
 * Version:  1.2
 * Copyright (C) 1995  Brian Paul  (brianp@ssec.wisc.edu)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/*
$Id: interp.c,v 1.7 1995/10/23 21:27:54 brianp Exp $

$Log: interp.c,v $
 * Revision 1.7  1995/10/23  21:27:54  brianp
 * new GLubyte interpolation using fixed point arithmetic
 *
 * Revision 1.6  1995/06/12  15:43:57  brianp
 * separate GLint and GLubyte interpolation functions
 *
 * Revision 1.5  1995/06/02  13:58:36  brianp
 * faster gl_interpolate(), tried gl_interpolate_rgba()
 *
 * Revision 1.4  1995/05/22  21:02:41  brianp
 * Release 1.2
 *
 * Revision 1.3  1995/03/17  19:15:21  brianp
 * tried to improve gl_interp_texcoords, not much luck
 *
 * Revision 1.2  1995/03/04  19:29:44  brianp
 * 1.1 beta revision
 *
 * Revision 1.1  1995/02/27  22:20:13  brianp
 * Initial revision
 *
 */


#include "context.h"
#include "macros.h"


/*#define TEST*/


/*
 * Linear integer interpolation:
 * Iterpolate n integer values between y0 and y1 and put into yspan.
 * When finished, yspan[0] = y0, yspan[n-1] = y1, and the rest of
 * yspan[] is filled with interpolated values.
 */
void gl_interpolate_i( GLint n, GLint y0, GLint y1, GLint yspan[] )
{
   switch (n) {
      case 1:
         yspan[0] = y0;
	 return;
      case 2:
         yspan[0] = y0;
         yspan[1] = y1;
	 return;
      case 3:
         yspan[0] = y0;
	 yspan[1] = (y0+y1) >> 1;
         yspan[2] = y1;
	 return;
      default:
	 if (y0==y1) {
	    register GLint i;
	    for (i=0;i<n;i++) {
	       yspan[i] = y0;
	    }
	 }
	 else {
	    register GLint i;
	    register GLint dx, dy;
	    register GLint a, b, d;
	    register GLint y;
	    register GLint qa, qb;
   	    dx = n-1;
	    dy = y1 - y0;
	    qa = dy / dx;
	    dy = dy % dx;
	    if (dy<0) {
	       dy = -dy;
	       qb = qa - 1;
	    }
	    else {
	       qb = qa + 1;
	    }
	    a = dy+dy;   d = a-dx;   b = d-dx;
	    y = y0;
	    for (i=0;i<n;i++) {
	       yspan[i] = y;
	       if (d<0) {
		  d += a;
		  y += qa;
	       }
	       else {
		  d += b;
		  y += qb;
	       }
	    }
	 }
   }
}




void gl_interpolate_ub( GLint n, GLint y0, GLint y1, GLubyte yspan[] )
{
   int i, dy;

   switch (n) {
      case 1:
         yspan[0] = y0;
         return;
      case 2:
         yspan[0] = y0;
         yspan[1] = y1;
         return;
      case 3:
         yspan[0] = y0;
         yspan[1] = (y0+y1) >> 2;
         yspan[2] = y1;
         return;
      default:
         y0 = y0 << 8;
         y1 = y1 << 8;
         dy = (y1-y0) / (n-1);
         for (i=0;i<n;i++) {
            yspan[i] = y0 >> 8;
            y0 += dy;
         }
         return;
   }
}



void gl_interpolate_4ub( GLint n,
                         GLint a0, GLint a1, GLubyte aspan[],
                         GLint b0, GLint b1, GLubyte bspan[],
                         GLint c0, GLint c1, GLubyte cspan[],
                         GLint d0, GLint d1, GLubyte dspan[] )
{
   GLint i, m;
   GLint da, db, dc, dd;

   switch (n) {
      case 1:
         aspan[0] = a0;
         bspan[0] = b0;
         cspan[0] = c0;
         dspan[0] = d0;
	 return;
      case 2:
         aspan[0] = a0;   aspan[1] = a1;
         bspan[0] = b0;   bspan[1] = b1;
         cspan[0] = c0;   cspan[1] = c1;
         dspan[0] = d0;   dspan[1] = d1;
	 return;
      case 3:
         aspan[0] = a0;   aspan[1] = (a0+a1)>>2;   aspan[2] = a1;
         bspan[0] = b0;   bspan[1] = (b0+b1)>>2;   bspan[2] = b1;
         cspan[0] = c0;   cspan[1] = (c0+c1)>>2;   cspan[2] = c1;
         dspan[0] = d0;   dspan[1] = (d0+d1)>>2;   dspan[2] = d1;
         return;
      default:
         a0 = a0 << 8;
         b0 = b0 << 8;
         c0 = c0 << 8;
         d0 = d0 << 8;
         m = n-1;
         da = ((a1<<8)-a0) / m;
         db = ((b1<<8)-b0) / m;
         dc = ((c1<<8)-c0) / m;
         dd = ((d1<<8)-d0) / m;
         for (i=0;i<n;i++) {
            aspan[i] = a0 >> 8;    a0 += da;
            bspan[i] = b0 >> 8;    b0 += db;
            cspan[i] = c0 >> 8;    c0 += dc;
            dspan[i] = d0 >> 8;    d0 += dd;
         }
         return;
   }
}




#ifndef TEST

/*
 * Perform texture coordinate interpolation along a line in window coord-
 * inate space.  Depending on the perspective correction hint we'll either
 * just do simple linear interpolation or interpolation with perspective
 * correction.
 * Input:  n - number of texture coords to produce
 *         eyez0, eyez1 - z coordinate of end points in eye coords
 *         winz0  winz1 - z coordinate of end points in window coords
 *         s0, s1 - S-component of texture coords at end points
 *         t0, t1 - T-component of texture coords at end points
 * Output:  s, t - resulting arrays of S and T texture coordinates
 *          z - interpolated Z component of eye coordinates along the line
 *              (note that z can be a NULL pointer if z isn't needed)
 */
void gl_interp_texcoords( GLuint n,
			  GLfloat eyez0, GLfloat eyez1,
			  GLfloat winz0, GLfloat winz1,
			  GLfloat s0, GLfloat s1,
			  GLfloat t0, GLfloat t1,
			  GLfloat s[], GLfloat t[], GLfloat *z )
{
   /* TODO: the main loop in this function could be optimized with */
   /* forward differences, etc.  However, since texture mapping is */
   /* so expensive, optimizing this might not make a big difference. */

   /* TODO: this function has a terrible problem with numerical error */
   /* under certain conditions, namely when interpolating along a vector */
   /* which is _nearly_ parallel to the viewplane. */

   GLfloat d = CC.ProjectionMatrix[14];
   GLfloat c = CC.ProjectionMatrix[10];
   GLfloat delta_eyez = eyez1 - eyez0;
   GLfloat delta_winz = winz1 - winz0;
   GLfloat delta_s = s1 - s0;
   GLfloat delta_t = t1 - t0;
   GLuint i;

   if (n==1) {
      s[0] = s0;
      t[0] = t0;
      if (z)  z[0] = eyez0;
      return;
   }

   if (CC.Hint.PerspectiveCorrection==GL_NICEST && d!=0.0
       && ABS(delta_eyez)>0.001) {
      /* interpolate with perspective compensation */
      GLfloat q, r, winz, ndcz, eyez;

      for (i=0;i<n;i++) {
	 q = (GLfloat) i / (n-1);
	 winz = (winz0 + q * delta_winz) / (GLfloat) MAX_DEPTH;

	 ndcz = (winz - CC.Viewport.Tz) / CC.Viewport.Sz;

	 eyez = -d / (c+ndcz);

	 r = (eyez - eyez0) / delta_eyez;
	 r = CLAMP( r, 0.0, 1.0 );    /* to catch over/underflow */

	 s[i] = s0 + r * delta_s;
	 t[i] = t0 + r * delta_t;
	 if (z) {
	    z[i] = eyez;
	 }
      }
   }
   else {
      /* simple linear interpolation */
      if (z) {
	 for (i=0;i<n;i++) {
	    GLfloat q = (GLfloat) i / (n-1);
	    s[i] = s0 + q * delta_s;
	    t[i] = t0 + q * delta_t;
	    z[i] = eyez0 + q * delta_eyez;
	 }
      }
      else {
	 for (i=0;i<n;i++) {
	    GLfloat q = (GLfloat) i / (n-1);
	    s[i] = s0 + q * delta_s;
	    t[i] = t0 + q * delta_t;
	 }
      }
   }
}

#endif


#ifdef TEST
static void foo( int n, int red[], int green[], int blue[], int alpha[] )
{

}



#define ITER 700
#define MAX 500

#include "interp.h"

main()
{
   int i, j, n, jj;

   for (i=0;i<ITER;i++) {
      int red[MAX], green[MAX], blue[MAX], alpha[MAX];
      jj = MAX2( 2, i );
      for (j=0;j<jj;j++) {
	 n = j/4+1;

	 GL_INTERPOLATE( n, j, n, red );
	 GL_INTERPOLATE( n, j+3, j*8, green );
	 GL_INTERPOLATE( n, i, j+i*2, blue );
	 GL_INTERPOLATE( n, i, i, alpha );
/*	 gl_interpolate_rgba( n, j, n, red,
	                         j+3, j*8, green,
                                 i, j+i*2, blue,
                                 i, i, alpha );
*/
      }
      foo( n, red, green, blue, alpha );
   }

}

#endif
