/*****************************************************************************
   Major portions of this software are copyrighted by the Medical College
   of Wisconsin, 1994-2000, and are released under the Gnu General Public
   License, Version 2.  See the file README.Copyright for details.
******************************************************************************/

#include "mrilib.h"

/*** NOT 7D SAFE ***/

/*! Return far[] if (i,j) is inside the image, otherwise return 0. */

#define FINS(i,j) (  ( (i)<0 || (j)<0 || (i)>=nx || (j)>=ny ) \
                     ? 0.0 : far[(i)+(j)*nx] )

/*-------------------------------------------------------------------*/
/*! Invert a 2D matrix, dude. */

static void invert2d( float  axx, float  axy, float  ayx, float  ayy ,
                      float *bxx, float *bxy, float *byx, float *byy  )
{
   float det = axx*ayy - axy*ayx ;
   if( det == 0.0 ){ *bxx=*byy=*bxy=*byx = 0.0 ; return ; }
   *bxx =  ayy / det ;
   *byy =  axx / det ;
   *bxy = -axy / det ;
   *byx = -ayx / det ;
   return ;
}

/*-------------------------------------------------------------------*/
/*! Affine transform a 2D image, using bilinear interpolation:
    If flag == 0
       [ xout ] = [ axx axy ] [ xin ]
       [ yout ] = [ ayx ayy ] [ yin ]
    If flag == 1
       [ xin ] = [ axx axy ] [ xout ]
       [ yin ] = [ ayx ayy ] [ yout ]
    These are index coordinates, not spatial.
----------------------------------------------------------------------*/

MRI_IMAGE *mri_aff2d_byte( MRI_IMAGE *im, int flag ,
                           float axx, float axy, float ayx, float ayy )
{
   float bxx,bxy,byx,byy , xbase,ybase , xx,yy , fx,fy ;
   float f_j00,f_jp1 , wt_00,wt_p1 ;
   int ii,jj , nx,ny , ix,jy ;
   MRI_IMAGE *newImg ;
   byte *far , *nar ;

ENTRY("mri_aff2d_byte") ;

   if( im == NULL || !MRI_IS_2D(im) || im->kind != MRI_byte ){
      fprintf(stderr,"*** mri_aff2d_byte only works on 2D byte images!\n");
      RETURN( NULL );
   }

   if( flag == 0 ){
      invert2d( axx,axy,ayx,ayy , &bxx,&bxy,&byx,&byy ) ;
   } else {
      bxx = axx ; bxy = axy ; byx = ayx ; byy = ayy ;
   }
   if( (bxx == 0.0 && bxy == 0.0) || (byx == 0.0 && byy == 0.0) ){
      fprintf(stderr,"*** mri_aff2d_byte: input matrix is singular!\n") ;
      RETURN( NULL );
   }

   nx = im->nx ; ny = im->ny ;
   xbase = 0.5*nx*(1.0-bxx) - 0.5*ny*bxy ;
   ybase = 0.5*ny*(1.0-byy) - 0.5*nx*byx ;

   far = MRI_BYTE_PTR(im) ;                /* input image data */
   newImg = mri_new( nx , nx , MRI_byte ) ;   /* output image */
   nar = MRI_BYTE_PTR(newImg) ;               /* output image data */

   /*** loop over output points and warp to them ***/

   for( jj=0 ; jj < nx ; jj++ ){
      xx = xbase-bxx + bxy * jj ;
      yy = ybase-byx + byy * jj ;
      for( ii=0 ; ii < nx ; ii++ ){

         xx += bxx ;  /* get x,y in original image */
         yy += byx ;

         ix = (xx >= 0.0) ? ((int) xx) : ((int) xx)-1 ;  /* floor */
         jy = (yy >= 0.0) ? ((int) yy) : ((int) yy)-1 ;

         fx = xx-ix ; wt_00 = 1.0 - fx ; wt_p1 = fx ;

         if( ix >= 0 && ix < nx-1 && jy >= 0 && jy < ny-1 ){
            byte *fy00 , *fyp1 ;

            fy00 = far + (ix + jy*nx) ; fyp1 = fy00 + nx ;

            f_j00 = wt_00 * fy00[0] + wt_p1 * fy00[1] ;
            f_jp1 = wt_00 * fyp1[0] + wt_p1 * fyp1[1] ;

         } else {
            f_j00 = wt_00 * FINS(ix,jy  ) + wt_p1 * FINS(ix+1,jy  ) ;
            f_jp1 = wt_00 * FINS(ix,jy+1) + wt_p1 * FINS(ix+1,jy+1) ;
         }

         fy  = yy-jy ; nar[ii+jj*nx] = (1.0-fy) * f_j00 + fy * f_jp1 ;

      }
   }

   MRI_COPY_AUX(newImg,im) ;
   RETURN( newImg ) ;
}

/*----------------------------------------------------------------------*/

/*! Same as FINS(), but for the R component of an RGB image. */

#define RINS(i,j) (  ( (i)<0 || (j)<0 || (i)>=nx || (j)>=ny ) \
                     ? 0.0 : far[3*((i)+(j)*nx)] )

/*! Same as FINS(), but for the G component of an RGB image. */

#define GINS(i,j) (  ( (i)<0 || (j)<0 || (i)>=nx || (j)>=ny ) \
                     ? 0.0 : far[3*((i)+(j)*nx)+1] )

/*! Same as FINS(), but for the B component of an RGB image. */

#define BINS(i,j) (  ( (i)<0 || (j)<0 || (i)>=nx || (j)>=ny ) \
                     ? 0.0 : far[3*((i)+(j)*nx)+2] )

/*----------------------------------------------------------------------*/
/*!  Same as mri_aff2d_byte(), but for RGB images [11 Dec 2000].
------------------------------------------------------------------------*/

MRI_IMAGE *mri_aff2d_rgb( MRI_IMAGE *im, int flag ,
                          float axx, float axy, float ayx, float ayy )
{
   float bxx,bxy,byx,byy , xbase,ybase , xx,yy , fx,fy ;
   float f_j00r,f_jp1r , f_j00g,f_jp1g , f_j00b,f_jp1b , wt_00,wt_p1 ;
   int jj , nx,ny , ix,jy ;
   MRI_IMAGE *newImg ;
   byte *far , *nar ;
   register int ii ;

ENTRY("mri_aff2d_rgb") ;

   if( im == NULL || !MRI_IS_2D(im) || im->kind != MRI_rgb ){
      fprintf(stderr,"*** mri_aff2d_rgb only works on 2D RGB images!\n");
      RETURN( NULL );
   }

   if( flag == 0 ){
      invert2d( axx,axy,ayx,ayy , &bxx,&bxy,&byx,&byy ) ;
   } else {
      bxx = axx ; bxy = axy ; byx = ayx ; byy = ayy ;
   }
   if( (bxx == 0.0 && bxy == 0.0) || (byx == 0.0 && byy == 0.0) ){
      fprintf(stderr,"*** mri_aff2d_byte: input matrix is singular!\n") ;
      RETURN( NULL );
   }

   nx = im->nx ; ny = im->ny ;
   xbase = 0.5*nx*(1.0-bxx) - 0.5*ny*bxy ;
   ybase = 0.5*ny*(1.0-byy) - 0.5*nx*byx ;

   far = MRI_RGB_PTR(im) ;                /* input image data */
   newImg = mri_new( nx , nx , MRI_rgb ) ;   /* output image */
   nar = MRI_RGB_PTR(newImg) ;               /* output image data */

   /*** loop over output points and warp to them ***/

   for( jj=0 ; jj < nx ; jj++ ){
      xx = xbase-bxx + bxy * jj ;
      yy = ybase-byx + byy * jj ;
      for( ii=0 ; ii < nx ; ii++ ){

         xx += bxx ;  /* get x,y in original image */
         yy += byx ;

         ix = (xx >= 0.0) ? ((int) xx) : ((int) xx)-1 ;  /* floor */
         jy = (yy >= 0.0) ? ((int) yy) : ((int) yy)-1 ;

         fx = xx-ix ; wt_00 = 1.0 - fx ; wt_p1 = fx ;

         if( ix >= 0 && ix < nx-1 && jy >= 0 && jy < ny-1 ){
            byte *fy00 , *fyp1 ;

            fy00 = far + 3*(ix+jy*nx) ; fyp1 = fy00 + 3*nx ;

            f_j00r = wt_00 * fy00[0] + wt_p1 * fy00[3] ;
            f_j00g = wt_00 * fy00[1] + wt_p1 * fy00[4] ;
            f_j00b = wt_00 * fy00[2] + wt_p1 * fy00[5] ;

            f_jp1r = wt_00 * fyp1[0] + wt_p1 * fyp1[3] ;
            f_jp1g = wt_00 * fyp1[1] + wt_p1 * fyp1[4] ;
            f_jp1b = wt_00 * fyp1[2] + wt_p1 * fyp1[5] ;

         } else {
            f_j00r = wt_00 * RINS(ix,jy  ) + wt_p1 * RINS(ix+1,jy  ) ;
            f_j00g = wt_00 * GINS(ix,jy  ) + wt_p1 * GINS(ix+1,jy  ) ;
            f_j00b = wt_00 * BINS(ix,jy  ) + wt_p1 * BINS(ix+1,jy  ) ;

            f_jp1r = wt_00 * RINS(ix,jy+1) + wt_p1 * RINS(ix+1,jy+1) ;
            f_jp1g = wt_00 * GINS(ix,jy+1) + wt_p1 * GINS(ix+1,jy+1) ;
            f_jp1b = wt_00 * BINS(ix,jy+1) + wt_p1 * BINS(ix+1,jy+1) ;
         }

         fy = yy-jy ;
         nar[3*ii+ 3*jj*nx   ] = (1.0-fy) * f_j00r + fy * f_jp1r ;
         nar[3*ii+(3*jj*nx+1)] = (1.0-fy) * f_j00g + fy * f_jp1g ;
         nar[3*ii+(3*jj*nx+2)] = (1.0-fy) * f_j00b + fy * f_jp1b ;

      }
   }

   MRI_COPY_AUX(newImg,im) ;
   RETURN( newImg );
}
