/*-
 * Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: imgscale.c,v 1.18 2002/05/14 13:22:28 dk Exp $
 */
/* Created by Dmitry Karasik <dk@plab.ku.dk> */
#include "img_conv.h"

#ifdef __cplusplus
extern "C" {
#endif


#define var (( PImage) self)

#define BS_BYTEIMPACT( type)                                                        \
void bs_##type##_in( type * srcData, type * dstData, int w, int x, int absx, long step)  \
{                                                                                   \
   Fixed count = {0};                                                               \
   int   last = 0;                                                                  \
   int   i;                                                                         \
   int   j    = ( x == absx) ? 0 : ( absx - 1);                                     \
   int   inc  = ( x == absx) ? 1 : -1;                                              \
   dstData[j] = srcData[0];                                                         \
   j += inc;                                                                        \
   for ( i = 0; i < w; i++)                                                         \
   {                                                                                \
      if ( count.i.i > last)                                                        \
      {                                                                             \
         dstData[j] = srcData[i];                                                   \
         j += inc;                                                                  \
         last = count. i. i;                                                        \
      }                                                                             \
      count. l += step;                                                             \
   }                                                                                \
}

#define BS_BYTEEXPAND( type)                                                        \
void bs_##type##_out( type * srcData, type * dstData, int w, int x, int absx, long step) \
{                                                                                   \
   Fixed count = {0};                                                               \
   int   i;                                                                         \
   int   j    = ( x == absx) ? 0 : ( absx - 1);                                     \
   int   inc  = ( x == absx) ? 1 : -1;                                              \
   int   last = 0;                                                                  \
   for ( i = 0; i < absx; i++)                                                      \
   {                                                                                \
      if ( count. i. i > last)                                                      \
      {                                                                             \
         last = count. i. i;                                                        \
         srcData++;                                                                 \
      }                                                                             \
      count. l += step;                                                             \
      dstData[j] = *srcData;                                                        \
      j += inc;                                                                     \
   }                                                                                \
}

BS_BYTEEXPAND( uint8_t)
BS_BYTEEXPAND( int16_t)
BS_BYTEEXPAND( RGBColor)
BS_BYTEEXPAND( int32_t)
BS_BYTEEXPAND( float)
BS_BYTEEXPAND( double)
BS_BYTEEXPAND( Complex)
BS_BYTEEXPAND( DComplex)

BS_BYTEIMPACT( uint8_t)
BS_BYTEIMPACT( int16_t)
BS_BYTEIMPACT( RGBColor)
BS_BYTEIMPACT( int32_t)
BS_BYTEIMPACT( float)
BS_BYTEIMPACT( double)
BS_BYTEIMPACT( Complex)
BS_BYTEIMPACT( DComplex)


void
bs_mono_in( uint8_t * srcData, uint8_t * dstData, int w, int x, int absx, long step)
{
   Fixed count = {0};
   int   last   = 0;
   register int i, j;
   register U16 xd, xs;

   if ( x == absx)
   {
      xd = (( xs = srcData[0]) >> 7) & 1;
      j = 1;
      for ( i = 0; i < w; i++)
      {
         if (( i & 7) == 0) xs = srcData[ i >> 3];
         xs <<= 1;
         if ( count.i.i > last)
         {
            if (( j & 7) == 0) dstData[ ( j - 1) >> 3] = xd;
            xd <<= 1;
            xd |= ( xs >> 8) & 1;
            j++;
            last = count. i. i;
         }
         count. l += step;
      }
      i = j & 7;
      dstData[( j - 1) >> 3] = xd << ( i ? ( 8 - i) : 0);
   } else {
      j = absx - 1;
      xd = ( xs = srcData[ j >> 3]) & 0x80;
      for ( i = 0; i < w; i++)
      {
         if (( i & 7) == 0) xs = srcData[ i >> 3];
         xs <<= 1;
         if ( count.i.i > last)
         {
            if (( j & 7) == 0) dstData[ ( j + 1) >> 3] = xd;
            xd >>= 1;
            xd |= ( xs >> 1) & 0x80;
            j--;
            last = count. i. i;
         }
         count. l += step;
      }
      dstData[( j + 1) >> 3] = xd;
   }
}

void
bs_mono_out( uint8_t * srcData, uint8_t * dstData, int w, int x, int absx, long step)
{
   Fixed    count = {0};
   register int i, j = 0;
   register U16 xd = 0, xs;
   int last = 0;

   if ( x == absx)
   {
      xs = srcData[0];
      for ( i = 0; i < absx; i++)
      {
         if ( count. i. i > last)
         {
            last = count.i.i;
            xs <<= 1;
            j++;
            if (( j & 7) == 0) xs = srcData[ j >> 3];
         }
         count.l += step;
         xd <<= 1;
         xd |= ( xs >> 7) & 1;
         if ((( i + 1) & 7) == 0) dstData[ i >> 3] = xd;
      }
      if ( i & 7) dstData[ i >> 3] = xd << ( 8 - ( i & 7));
      /* dstData[ i >> 3] = xd << (( i & 7) ? ( 8 - ( i & 7)) : 0); */
   } else {
      register int k = absx;
      xs = srcData[ j >> 3];
      for ( i = 0; i < absx; i++)
      {
         if ( count. i. i > last)
         {
            last = count.i.i;
            xs <<= 1;
            j++;
            if (( j & 7) == 0) xs = srcData[ j >> 3];
         }
         count.l += step;
         xd >>= 1;
         xd |= xs & 0x80;
         k--;
         if (( k & 7) == 0) dstData[( k + 1) >> 3] = xd;
      }
      dstData[( k + 0) >> 3] = xd;
   }
}

/* nibble stretching functions are requiring *dstData filled with zeros */

void bs_nibble_in( uint8_t * srcData, uint8_t * dstData, int w, int x, int absx, long step)
{
   Fixed count = {0};
   int   last = 0;
   int   i;
   int   j    = ( x == absx) ? 0 : ( absx - 1);
   int   inc  = ( x == absx) ? 1 : -1;
   dstData[ j >> 1] |= ( j & 1) ? ( srcData[ 0] >> 4) : ( srcData[ 0] & 0xF0);
   j += inc;
   for ( i = 0; i < w; i++)
   {
      if ( count.i.i > last)
      {
         if ( i & 1)
            dstData[ j >> 1] |= ( j & 1) ? ( srcData[ i >> 1] & 0x0F) : ( srcData[ i >> 1] << 4);
         else
            dstData[ j >> 1] |= ( j & 1) ? ( srcData[ i >> 1] >> 4) : ( srcData[ i >> 1] & 0xF0);
         j += inc;
         last = count. i. i;
      }
      count. l += step;
   }
}

void bs_nibble_out( uint8_t * srcData, uint8_t * dstData, int w, int x, int absx, long step)
{
   Fixed count = {0};
   int   i, k = 0;
   int   j    = ( x == absx) ? 0 : ( absx - 1);
   int   inc  = ( x == absx) ? 1 : -1;
   int   last = 0;
   for ( i = 0; i < absx; i++)
   {
      if ( count. i. i > last)
      {
         if (( k & 1) == 1) srcData++;
         k++;
         last = count. i. i;
      }
      count. l += step;
      if ( k & 1)
         dstData[ j >> 1] |= ( j & 1) ? ( *srcData & 0x0F) : ( *srcData << 4);
      else
         dstData[ j >> 1] |= ( j & 1) ? ( *srcData >> 4) : ( *srcData & 0xF0);
      j += inc;
   }
}

void
ic_stretch( int type, Byte * srcData, int srcW, int srcH, Byte * dstData, int w, int h, Bool xStretch, Bool yStretch)
{
   int  absh = h < 0 ? -h : h;
   int  absw = w < 0 ? -w : w;
   int  srcLine = (( srcW *  ( type & imBPP) + 31) / 32) * 4;
   int  dstLine = (( absw  * ( type & imBPP) + 31) / 32) * 4;
   Fixed xstep, ystep, count;
   int last = 0;
   int i;
   int yMin = ( srcH > absh) ? absh : srcH;
   PStretchProc proc = nil;
   Byte *srcLast = nil;

   if ( w == srcW) xStretch = false;
   if ( h == srcH) yStretch = false;
/* transfer case */
   if ( !xStretch && !yStretch && ( w > 0))
   {
      int y;
      int xMin = (( type & imBPP) < 8) ? 
                  ( srcLine > dstLine) ? dstLine : srcLine :
                  (((( srcW > absw) ? absw : srcW) * ( type & imBPP)) / 8);
      if ( srcW < w || srcH < absh) memset( dstData, 0, dstLine * absh);
      if ( h < 0)
      {
         dstData += dstLine * ( yMin - 1);
         dstLine =- dstLine;
      }
      for ( y = 0; y < yMin; y++, srcData += srcLine, dstData += dstLine) 
         memcpy( dstData, srcData, xMin);
      return;
   }

/* y-only stretch case */
   if ( !xStretch && yStretch && ( w > 0))
   {
      int xMin = (( type & imBPP) < 8) ? 
         ( srcLine > dstLine) ? dstLine : srcLine :
         (((( srcW > absw) ? absw : srcW) * ( type & imBPP)) / 8);
      count. l = 0;
      if ( srcW < w) memset( dstData, 0, dstLine * absh);
      if ( h < 0)
      {
         dstData += dstLine * ( absh - 1);
         dstLine =- dstLine;
      }
      if ( absh < srcH)
      {
         ystep. l = (double) absh / srcH * UINT16_PRECISION;
         memcpy( dstData, srcData, xMin);
         dstData += dstLine;
         for ( i = 0; i < srcH; i++)
         {
            if ( count. i.i > last)
            {
               memcpy( dstData, srcData, xMin);
               dstData += dstLine;
               last = count.i.i;
            }
            count. l += ystep. l;
            srcData += srcLine;
         }
      } else {
         ystep. l = (double) srcH / absh * UINT16_PRECISION;
         for ( i = 0; i < absh; i++)
         {
            if ( count.i.i > last)
            {
               srcData += srcLine;
               last = count.i.i;
            }
            count. l += ystep. l;
            memcpy( dstData, srcData, xMin);
            dstData  += dstLine;
         }
      }
      return;
   }

/* general actions for x-scaling */
   count. l = 0;
   count. l = 0;
   if ( srcW < absw || srcH < absh || ( type & imBPP) == imNibble)
      memset( dstData, 0, dstLine * absh);
   if ( absw < srcW)
      xstep. l = (double) absw / srcW * UINT16_PRECISION;
   else
      xstep. l = (double) srcW / absw * UINT16_PRECISION;
   switch( type)
   {
      case imMono:     case imBW:
         proc = ( PStretchProc)(( srcW > absw) ? bs_mono_in : bs_mono_out);  break;
      case imNibble:   case imNibble|imGrayScale:
         proc = ( PStretchProc)(( srcW > absw) ? bs_nibble_in : bs_nibble_out);     break;
      case imByte:     case im256:
         proc = ( PStretchProc)(( srcW > absw) ? bs_uint8_t_in : bs_uint8_t_out);   break;
      case imRGB:      case imRGB|imGrayScale:
         proc = ( PStretchProc)(( srcW > absw) ? bs_RGBColor_in : bs_RGBColor_out); break;
      case imShort:
         proc = ( PStretchProc)(( srcW > absw) ? bs_int16_t_in : bs_int16_t_out);   break;
      case imLong:
         proc = ( PStretchProc)(( srcW > absw) ? bs_int32_t_in : bs_int32_t_out);   break;
      case imFloat:
         proc = ( PStretchProc)(( srcW > absw) ? bs_float_in : bs_float_out);       break;
      case imDouble:
         proc = ( PStretchProc)(( srcW > absw) ? bs_double_in : bs_double_out);     break;
      case imComplex:  case imTrigComplex:
         proc = ( PStretchProc)(( srcW > absw) ? bs_Complex_in : bs_Complex_out);   break;
      case imDComplex: case imTrigDComplex:
         proc = ( PStretchProc)(( srcW > absw) ? bs_DComplex_in : bs_DComplex_out); break;
      default:
         return;
   }

/* no vertical stretch case */
   if ( !yStretch || ( srcH == -h))
   {
      if ( h < 0)
      {
         dstData += dstLine * ( yMin - 1);
         dstLine =- dstLine;
      }
      for ( i = 0; i < srcH; i++, srcData += srcLine, dstData += dstLine)
         proc( srcData, dstData, srcW, w, absw, xstep.l);
      return;
   }

/* general case */
   if ( h < 0)
   {
      dstData += dstLine * ( absh - 1);
      dstLine =- dstLine;
   }
   if ( absh < srcH)
   {
      ystep. l = (double) absh / srcH * UINT16_PRECISION;
      proc( srcData, dstData, srcW, w, absw, xstep.l);
      dstData += dstLine;
      for ( i = 0; i < srcH; i++)
      {
         if ( count. i.i > last)
         {
            proc( srcData, dstData, srcW, w, absw, xstep.l);
            dstData += dstLine;
            last = count.i.i;
         }
         count. l += ystep. l;
         srcData += srcLine;
      }
   } else {
      ystep. l = (double) srcH / absh * UINT16_PRECISION;
      for ( i = 0; i < absh; i++)
      {
         if ( count.i.i > last)
         {
            srcData += srcLine;
            last = count.i.i;
         }
         count. l += ystep. l;
         if ( srcLast == srcData) {
            memcpy( dstData, dstData - dstLine, dstLine < 0 ? -dstLine : dstLine);
         } else {
            proc( srcData, dstData, srcW, w, absw, xstep.l);
            srcLast = srcData;
         }
         dstData += dstLine;
      }
   }
}

#ifdef __cplusplus
}
#endif
