/*
        Frank Lyonnet 1993
        Module Scaling
*/

#include <stdlib.h>
#include "FYEOMem.h"
#include "Bool.h"
#include "Scaling.h"
#include <math.h>                            
#include "Erreur.h"
#include "kernel.h"
#include "MyThread.h"                                            
#include <string.h>
#include "Scaler.h"
#include "MathUtils.h"

FYEO_scaling * FYEO_color_scaling_init(int OutWidth , int OutHeight , int XMul , int XDiv ,
   int YMul , int YDiv , void ( * PutOneLine)(char *,char *,char *) , BOOL SpecialSpeed )
{
FYEO_scaling * Scaling;                          

   if((Scaling = (FYEO_scaling *)FYEO_alloc(sizeof(FYEO_scaling)))==NULL)
   {
      erreur("Not enough memory for scaling buffer");
      my_thread_leave(ERREUR);
   };

   Scaling -> OutWidth = OutWidth;
   Scaling -> OutHeight = OutHeight;
   Scaling -> PutOneColorLine = *PutOneLine;
   Scaling -> SpecialSpeed = SpecialSpeed;     

   /* Here we deal with colour scaling */
   Scaling -> PutOneGrayLine = NULL;
                                         
   /* Get scaling factors */
   Scaling -> NumX = XMul;
   Scaling -> DivX = XDiv;
   Scaling -> NumY = YMul;
   Scaling -> DivY = YDiv;

   /* Is it a jpeg preview ? */
   Scaling -> Preview = SpecialSpeed && XMul == 1 && YMul == 1 && XDiv == 8 && YDiv == 8;

   /* Determine which type of scaling */
   Scaling -> MagX = ( (float) Scaling -> NumX / (float) Scaling -> DivX ) > 1;
   Scaling -> MagY = ( (float) Scaling -> NumY / (float) Scaling -> DivY ) > 1;
   Scaling -> RedX = ( (float) Scaling -> DivX / (float) Scaling -> NumX ) > 1;
   Scaling -> RedY = ( (float) Scaling -> DivY / (float) Scaling -> NumY ) > 1;

   /* Is there a scaling ? */
   Scaling -> Scaling = Scaling->MagX||Scaling->MagY||Scaling->RedX||Scaling->RedY;

   /* No lines in horizontal scaling buffer */
   Scaling -> LinesInHBuffer = 0;
              
   /* If scaling , alloc horizontal buffers */
   if(Scaling -> Scaling && !Scaling -> Preview)
   {                               
      /* HBuffer buffers are used for storing horizontally scaled lines */
      if((Scaling -> HBuffer1 = (char *)FYEO_alloc(OutWidth*Scaling -> DivY)) == NULL)
      {
         erreur("Not enough memory for scaling buffer");
         my_thread_leave(ERREUR);
      };
       
      if((Scaling -> HBuffer2 = (char *)FYEO_alloc(OutWidth*Scaling -> DivY)) == NULL)
      {
         erreur("Not enough memory for scaling buffer");
         my_thread_leave(ERREUR);
      };                  

      if((Scaling -> HBuffer3 = (char *)FYEO_alloc(OutWidth*Scaling -> DivY)) == NULL)
      {
         erreur("Not enough memory for scaling buffer");
         my_thread_leave(ERREUR);
      };
                 
      /* If vertical scaling , alloc vertical buffers */           
      if((Scaling -> MagY || Scaling -> RedY) && !Scaling -> Preview)
      {
         /* VBuffer buffers are used for storing horizontaly and verticaly scaled lines */
         if((Scaling -> VBuffer1 = (char *)FYEO_alloc(OutWidth*Scaling -> NumY)) == NULL)
         {
            erreur("Not enough memory for scaling buffer");
            my_thread_leave(ERREUR);
         };

         if((Scaling -> VBuffer2 = (char *)FYEO_alloc(OutWidth*Scaling -> NumY)) == NULL)
         {
            erreur("Not enough memory for scaling buffer");
            my_thread_leave(ERREUR);
         };

         if((Scaling -> VBuffer3 = (char *)FYEO_alloc(OutWidth*Scaling -> NumY)) == NULL)
         {
            erreur("Not enough memory for scaling buffer");
            my_thread_leave(ERREUR);
         };
      };                               
   };

   if(Scaling -> Preview)
   {
      /* One line is sufficient for preview */
      if((Scaling -> HBuffer1 = (char *)FYEO_alloc(Scaling -> OutWidth)) == NULL)
      {
         erreur("Not enough memory for scaling buffer");
         my_thread_leave(ERREUR);
      };

      if((Scaling -> HBuffer2 = (char *)FYEO_alloc(Scaling -> OutWidth)) == NULL)
      {
         erreur("Not enough memory for scaling buffer");
         my_thread_leave(ERREUR);
      };

      if((Scaling -> HBuffer3 = (char *)FYEO_alloc(Scaling -> OutWidth)) == NULL)
      {
         erreur("Not enough memory for scaling buffer");
         my_thread_leave(ERREUR);
      };
   };

   return(Scaling);
}


FYEO_scaling * FYEO_gray_scaling_init(int OutWidth , int OutHeight , int XMul , int XDiv ,
   int YMul , int YDiv , void ( * PutOneLine)(char *) , BOOL SpecialSpeed )
{
FYEO_scaling * Scaling;

   if((Scaling = (FYEO_scaling *)FYEO_alloc(sizeof(FYEO_scaling)))==NULL)
   {
      erreur("Not enough memory for scaling buffer");
      my_thread_leave(ERREUR);
   };

   Scaling -> OutWidth = OutWidth;
   Scaling -> OutHeight = OutHeight;
   Scaling -> PutOneColorLine = NULL;
   Scaling -> SpecialSpeed = SpecialSpeed;     

   /* Here we deal with gray scaling */
   Scaling -> PutOneGrayLine = *PutOneLine;
                                         
   /* Get scaling factors */
   Scaling -> NumX = XMul;
   Scaling -> DivX = XDiv;
   Scaling -> NumY = YMul;
   Scaling -> DivY = YDiv;

   /* Is it a jpeg preview ? */
   Scaling -> Preview = SpecialSpeed && XMul == 1 && YMul == 1 && XDiv == 8 && YDiv == 8;

   /* Determine which type of scaling */
   Scaling -> MagX = ( (float) Scaling -> NumX / (float) Scaling -> DivX ) > 1;
   Scaling -> MagY = ( (float) Scaling -> NumY / (float) Scaling -> DivY ) > 1;
   Scaling -> RedX = ( (float) Scaling -> DivX / (float) Scaling -> NumX ) > 1;
   Scaling -> RedY = ( (float) Scaling -> DivY / (float) Scaling -> NumY ) > 1;

   /* Is there a scaling ? */
   Scaling -> Scaling = Scaling->MagX||Scaling->MagY||Scaling->RedX||Scaling->RedY;

   /* No lines in horizontal scaling buffer */
   Scaling -> LinesInHBuffer = 0;
              
   /* If scaling , alloc horizontal buffers */
   if(Scaling -> Scaling && !Scaling -> Preview)
   {                               
      /* HBuffer buffers are used for storing horizontally scaled lines */
      if((Scaling -> HBuffer1 = (char *)FYEO_alloc(OutWidth*Scaling -> DivY)) == NULL)
      {
         erreur("Not enough memory for scaling buffer");
         my_thread_leave(ERREUR);
      };
                        
      /* If vertical scaling , alloc vertical buffers */           
      if(Scaling -> MagY || Scaling -> RedY)
      {
         /* VBuffer buffers are used for storing horizontaly and verticaly scaled lines */
         if((Scaling -> VBuffer1 = (char *)FYEO_alloc(OutWidth*Scaling -> NumY)) == NULL)
         {
            erreur("Not enough memory for scaling buffer");
            my_thread_leave(ERREUR);
         };
      };                               
   };

   if(Scaling -> Preview)
   {
      /* One line is sufficient for preview */
      if((Scaling -> HBuffer1 = (char *)FYEO_alloc(Scaling -> OutWidth)) == NULL)
      {
         erreur("Not enough memory for scaling buffer");
         my_thread_leave(ERREUR);
      };
   };
      
   return(Scaling);
}

void FYEO_color_scaling_add_one_line(FYEO_scaling * Scaling , char *InRed,char *InGreen,char *InBlue)
{
int i;

   if(Scaling -> Preview)
   {
      if(Scaling -> LinesInHBuffer == 0)
      {
         for(i=0 ; i < Scaling -> OutWidth;i++)
         {
            Scaling -> HBuffer1[i]=InRed[i*8];
            Scaling -> HBuffer2[i]=InGreen[i*8];
            Scaling -> HBuffer3[i]=InBlue[i*8];
         };
         Scaling -> PutOneColorLine(Scaling -> HBuffer1,Scaling -> HBuffer2,Scaling -> HBuffer3);
         Scaling -> LinesInHBuffer ++;
      }
      else
      {
         Scaling -> LinesInHBuffer ++;
         if(Scaling -> LinesInHBuffer == 8)
            Scaling -> LinesInHBuffer = 0;
      };     
      return;
   };
     
   if(Scaling -> Scaling) 
   {

      /* Scaling needed */

      if(Scaling -> MagX || Scaling -> RedX)
      {

         /* Horizontal scaling needed */

         if(Scaling -> RedX)
         {

            /* Do horizontal reduction from In buffers to HBuffer buffers */

            FYEO_hcomponent_reduction(InRed , Scaling -> HBuffer1 + Scaling -> LinesInHBuffer * Scaling -> OutWidth ,
               Scaling -> NumX , Scaling -> DivX , Scaling -> OutWidth);
            FYEO_hcomponent_reduction(InGreen , Scaling -> HBuffer2 + Scaling -> LinesInHBuffer * Scaling -> OutWidth ,
               Scaling -> NumX , Scaling -> DivX , Scaling -> OutWidth);
            FYEO_hcomponent_reduction(InBlue , Scaling -> HBuffer3 + Scaling -> LinesInHBuffer * Scaling -> OutWidth ,
               Scaling -> NumX , Scaling -> DivX, Scaling -> OutWidth);
         }
         else
         {

            /* Do horizontal expansion from In buffers to HBuffer buffers */

            FYEO_hcomponent_expansion(InRed , Scaling -> HBuffer1 + Scaling -> LinesInHBuffer * Scaling -> OutWidth ,
               Scaling -> NumX , Scaling -> DivX , Scaling -> OutWidth);
            FYEO_hcomponent_expansion(InGreen , Scaling -> HBuffer2 + Scaling -> LinesInHBuffer * Scaling -> OutWidth ,
               Scaling -> NumX , Scaling -> DivX , Scaling -> OutWidth);
            FYEO_hcomponent_expansion(InBlue , Scaling -> HBuffer3 + Scaling -> LinesInHBuffer * Scaling -> OutWidth ,
               Scaling -> NumX , Scaling -> DivX , Scaling -> OutWidth);
         };
      }
      else
      {

         /* Do a mem copy from In buffers to HBuffer buffers */

         memcpy(Scaling -> HBuffer1 + Scaling -> LinesInHBuffer * Scaling -> OutWidth , InRed ,
            Scaling -> OutWidth);
         memcpy(Scaling -> HBuffer2 + Scaling -> LinesInHBuffer * Scaling -> OutWidth , InGreen ,
            Scaling -> OutWidth);
         memcpy(Scaling -> HBuffer3 + Scaling -> LinesInHBuffer * Scaling -> OutWidth , InBlue ,
            Scaling -> OutWidth);        
      };

      /* One more line in HBuffer buffers */
      Scaling -> LinesInHBuffer ++;

      if(Scaling -> MagY || Scaling -> RedY)
      {

         /* Vertical scaling needed */

         if(Scaling -> RedY)
         {
            if(Scaling -> LinesInHBuffer == Scaling -> DivY)
            {

               /* HBuffer buffers are full */
                                               
               /* Do vertical reduction */                       
                  
               FYEO_vcomponent_reduction(Scaling -> HBuffer1 , Scaling -> VBuffer1 ,
                  Scaling -> NumY , Scaling -> DivY , Scaling -> OutWidth);
               FYEO_vcomponent_reduction(Scaling -> HBuffer2 , Scaling -> VBuffer2 ,
                  Scaling -> NumY , Scaling -> DivY , Scaling -> OutWidth);
               FYEO_vcomponent_reduction(Scaling -> HBuffer3 , Scaling -> VBuffer3 ,
                  Scaling -> NumY , Scaling -> DivY , Scaling -> OutWidth);

               /* Output vertical buffers ( size is NumY lines ) */

               for(i = 0; i < Scaling -> NumY ; i++)              
                  Scaling -> PutOneColorLine(Scaling -> VBuffer1 + i * Scaling -> OutWidth,
                     Scaling -> VBuffer2 + i * Scaling -> OutWidth,
                     Scaling -> VBuffer3 + i * Scaling -> OutWidth);
               Scaling -> LinesInHBuffer = 0;
            };
         }
         else   
         {
            if(Scaling -> LinesInHBuffer == Scaling -> DivY)
            {

               /* HBuffer buffers are full */
                                               
               /* Do vertical expansion */       

               FYEO_vcomponent_expansion(Scaling -> HBuffer1 , Scaling -> VBuffer1 ,
                  Scaling -> NumY , Scaling -> DivY , Scaling -> OutWidth);
               FYEO_vcomponent_expansion(Scaling -> HBuffer2 , Scaling -> VBuffer2 ,
                  Scaling -> NumY , Scaling -> DivY , Scaling -> OutWidth);
               FYEO_vcomponent_expansion(Scaling -> HBuffer3 , Scaling -> VBuffer3 ,
                  Scaling -> NumY , Scaling -> DivY , Scaling -> OutWidth);

               /* Output vertical buffers ( size is NumY lines ) */

               for(i = 0; i < Scaling -> NumY ; i++)
                  Scaling -> PutOneColorLine(Scaling -> VBuffer1 + i * Scaling -> OutWidth,
                     Scaling -> VBuffer2 + i * Scaling -> OutWidth,
                     Scaling -> VBuffer3 + i * Scaling -> OutWidth);
               Scaling -> LinesInHBuffer = 0;
            };
         };
      }
      else
      {
        
         /* No vertical scaling needed */

         /* Output horizontal buffer ( size is 1 line ) */

         Scaling -> PutOneColorLine(Scaling -> HBuffer1 , Scaling -> HBuffer2 , Scaling -> HBuffer3);
         Scaling -> LinesInHBuffer = 0;
      };
   }
   else
   {
      /* No scaling needed */

      /* Output In buffers */

      Scaling -> PutOneColorLine(InRed,InGreen,InBlue);
   };
}

void FYEO_gray_scaling_add_one_line(FYEO_scaling * Scaling , char *InGray)
{
int i;

   if(Scaling -> Preview)
   {
      if(Scaling -> LinesInHBuffer == 0)
      {
         for(i=0 ; i < Scaling->OutWidth; i++)
         {
            Scaling -> HBuffer1[i]=InGray[i*8];
         };
         Scaling -> PutOneGrayLine(Scaling -> HBuffer1);
         Scaling -> LinesInHBuffer ++;
      }
      else
      {
         Scaling -> LinesInHBuffer ++;
         if(Scaling -> LinesInHBuffer == 8)
            Scaling -> LinesInHBuffer = 0;
      };     
      return;
   };


   if(Scaling -> Scaling) 
   {

      /* Scaling needed */

      if(Scaling -> MagX || Scaling -> RedX)
      {

         /* Horizontal scaling needed */

         if(Scaling -> RedX)
         {

            /* Do horizontal reduction from In buffers to HBuffer buffers */

            FYEO_hcomponent_reduction(InGray , Scaling -> HBuffer1 + Scaling -> LinesInHBuffer * Scaling -> OutWidth ,
               Scaling -> NumX , Scaling -> DivX , Scaling -> OutWidth);
         }
         else
         {

            /* Do horizontal expansion from In buffers to HBuffer buffers */

            FYEO_hcomponent_expansion(InGray , Scaling -> HBuffer1 + Scaling -> LinesInHBuffer * Scaling -> OutWidth ,
               Scaling -> NumX , Scaling -> DivX , Scaling -> OutWidth);
         };
      }
      else
      {

         /* Do a mem copy from In buffers to HBuffer buffers */

         memcpy(Scaling -> HBuffer1 + Scaling -> LinesInHBuffer * Scaling -> OutWidth , InGray ,
            Scaling -> OutWidth);
      };

      /* One more line in HBuffer buffers */
      Scaling -> LinesInHBuffer ++;

      if(Scaling -> MagY || Scaling -> RedY)
      {

         /* Vertical scaling needed */

         if(Scaling -> RedY)
         {
            if(Scaling -> LinesInHBuffer == Scaling -> DivY)
            {

               /* HBuffer buffers are full */
                                               
               /* Do vertical reduction */                       
                  
               FYEO_vcomponent_reduction(Scaling -> HBuffer1 , Scaling -> VBuffer1 ,
                  Scaling -> NumY , Scaling -> DivY , Scaling -> OutWidth);

               /* Output vertical buffers ( size is NumY lines ) */

               for(i = 0; i < Scaling -> NumY ; i++)              
                  Scaling -> PutOneGrayLine(Scaling -> VBuffer1 + i * Scaling -> OutWidth);
               Scaling -> LinesInHBuffer = 0;
            };
         }
         else   
         {
            if(Scaling -> LinesInHBuffer == Scaling -> DivY)
            {

               /* HBuffer buffers are full */
                                               
               /* Do vertical expansion */       

               FYEO_vcomponent_expansion(Scaling -> HBuffer1 , Scaling -> VBuffer1 ,
                  Scaling -> NumY , Scaling -> DivY , Scaling -> OutWidth);

               /* Output vertical buffers ( size is NumY lines ) */

               for(i = 0; i < Scaling -> NumY ; i++)
                  Scaling -> PutOneGrayLine(Scaling -> VBuffer1 + i * Scaling -> OutWidth);
               Scaling -> LinesInHBuffer = 0;
            };
         };
      }
      else
      {
        
         /* No vertical scaling needed */

         /* Output horizontal buffer ( size is 1 line ) */

         Scaling -> PutOneGrayLine(Scaling -> HBuffer1);
         Scaling -> LinesInHBuffer = 0;
      };
   }
   else
   {
      /* No scaling needed */

      /* Output In buffers */

      Scaling -> PutOneGrayLine(InGray);
   };
}

void FYEO_color_scaling_term(FYEO_scaling * Scaling)
{
/* Done by FYEO_mem_term */
}

void FYEO_gray_scaling_term(FYEO_scaling * Scaling)
{
/* Done by FYEO_mem_term */
}
