/******************************************************************* 
 * Resize.c                                                        *
 * ========                                                        *
 *                                                                 * 
 * USE:                                                            * 
 * Resize newImgWidth newImgHeight printer halftone interp         * 
 *                                                                 * 
 *        interp = { repeat, avg }                                 * 
 *        dimensions given in pixels                               *
 *                                                                 * 
 *                                                                 * 
 * DESRIPTION:                                                     * 
 * This program takes an image, its original width and height in   *
 * number of pixels, and the desired width and height in number of *
 * pixels.  It will create a new image of the desired width and    *
 * height, using one of the following algorithms:                  *
 *                                                                 *
 *                1.  Repeated pixel                                *
 *                2.  Weighted averaging                           *
 *                                                                 *
 * For non-integer scaling, the program will first interpolate to  * 
 * enlarge the image, then sample.                                 *
 *                                                                 *
 *                                                                 *
 * REVISIONS:                                                      *
 * A   JPK  3/5/97   Assuming we get image string                  *
 *                   Pointers ARE used.                            *
 *                   Extract rgb components                        *
 * B   JPK  3/7/97   Expand by repeated pixel done                 *
 * C   JPK  3/8/97   Expand by interpolation/avg done              *
 * D   JPK  3/8/97   Sampling done                                 *
 * E   JPK  3/10/97  Finally!  get_hinfo works!                    *
 * F   JPK  3/10/97  get_pinfo works.                              *
 *                   get_hinfo assumes linear matrix mapping       *
 * G   JPK  3/10/97  CMYK calibration now assumes exp mapping      *
 *                   RGB to XYZ conversion works                   *
 * H   JPK  3/11/97  XYZ2CMYK tested.  print statements removed    *
 *                   from get_number.  get_number changed to       *
 *                   return a value.  Fixed bug in getting DPI     *
 *                   values.  Problems with pointers and memory    *
 *                   allocation fixed.                             *
 * I   JPK  3/11/97  Code complete except for piping info in and   *
 *                   out.  Print statements available for viewing  *
 *                   results.                                      *
 * J   JPK  3/12/97  Results printed to stdout.  Order of          *
 *                   arguments passed to function changed.         *
 * L   JPK  3/12/97  Pipe in image                                 *
 * M   JPK  3/13/97  Runs a little faster...no need to call        *
 *                   extract_rgb since image piped in directly to  *
 *                   RGB matrices.  Fixed sample ratio to be float *
 *                   in both resize routines.  Also changed the    *
 *                   sample increment and position to be floats,   *
 *                   only to be converted to int before usage      *
 * N   JPK  3/14/97  Updated printer calibration:  conversion of   *
 *                   monitor XYZ to amounts of absorption based on *
 *                   printer/paper white point's XYZ.  Read in     *
 *                   image width and height from image file.       *
 *                   Changed arguments to be passed to function.   *
 *                   Fixed bug with padding.  Can use different    *
 *                   functions for tone reproduction correction    *
 * O   JPK  3/14/97  Fixed get_number to account for neg. numbers  *
 * P   JPK  3/14/97  Scaling of XYZ for white point and truncation *
 * Q   JPK  3/14/97  New functions for error diffusion             *
 *******************************************************************/


#include 
#include 
#include 
#include 
#include 


#define  REPEAT 0
#define  AVG 1
#define  GREY 1
#define  COLOR 2
#define  PI 3.14159265359

/*******************************************************************
 * get_number                                                      *
 * ==========                                                      *
 * Takes a string and removes the first number in that string.     *
 * It updates the string so that it is pointing to the first       *
 * position after the number and returns that updated string       *
 * in UpdatedString.  Function also returns the number.            * 
 *******************************************************************/ 
float get_number(String, UpdatedString)
char *String;
char *UpdatedString;
{
  char c;
  char *IntString, *DecString;
  char *BeginIntString, *BeginDecString, *BeginString;
  int Mult, Count;
  int temp, negative;
  float Number;
  

  /* Initialize variables */
  negative = 0;
  IntString = malloc(sizeof(char) * strlen(String));
  DecString = malloc(sizeof(char) * strlen(String));

  BeginIntString = IntString;
  BeginDecString = DecString;
  
  Number = 0;

  /* Remove any commas, white spaces, etc., so that String
   * points to the first number remaining the line.  Since
   * the number could be less than one and start with a decimal
   * point, function also checks for decimal points.  Number
   * could also be negative number.
   */
  while ((isdigit(*String) == 0) && (*String != '.') && (*String != '-'))
    {
      String++;
    }

  /* As long as String has valid numerical values, add 
   * that to IntString (this is the integer part of the
   * number).
   */
  Count = 0;
  BeginString = String;
  while ((isdigit(*String) != 0) || (*String == '-'))
    {
      if (*String == '-')
	{
	  negative = 1;
	  BeginString++;
	  Count--;
	}
      Count++;
      String++;
    }
     
  strncpy(IntString, BeginString, Count);
  IntString[Count] = '\0';  /* Terminate string */
 
 /* If there is a decimal portion to the number,
   * put that part of the string into DecString.
   */

  Count = 0;
  BeginString = String;

  if (*String == '.')
    {
      String++;
      BeginString++;

      while (isdigit(*String) != 0)
	{
	  Count++;
	  String++;
	}
    }
  
  strncpy(DecString, BeginString, Count);
  DecString[Count] = '\0';    /* Terminate string */

  /* Make UpdatedString point to current position of String */
  strcpy(UpdatedString,String);

  /* Reset pointers for DecString and IntString to point
   * to the beginning of their respective strings
   */
  DecString = BeginDecString;
  IntString = BeginIntString;
  

  /* Now convert the strings representing the number into
   * an actual floating point representation
   */ 

  Number = atoi(IntString);
  Mult = strlen(DecString);
  
  Number = Number + pow(0.10, (float)Mult) * atoi(DecString);
  
  if (negative == 1)
    Number = Number * (-1);
     
  /* Free up memory */
  free (DecString);
  free (IntString); 

  return Number;
}
  
  
      

/*******************************************************************
 * get_pinfo                                                       *
 * =========                                                       *
 * Extracts the information needed to transform XYZ to CMYK for    * 
 * the particular printer specified.  CConv, MConv, YConv, KConv   *
 * are 1D arrays containing 3 elements each.  Multiplying the 1D   *
 * array by XYZ produces C, M, Y, or K.                            *
 *******************************************************************/ 
void get_pinfo(FileName, RConv, GConv, BConv, GreyDPI, ColorDPI, WhiteXYZ)
char *FileName;
float RConv[];
float GConv[];
float BConv[];
int GreyDPI[];
int ColorDPI[];
float WhiteXYZ[];
{
  FILE *FormatFile;
  char *TextLine, *LineMarker, *NewLineMarker;
  int MaxLine, Index, Mode;
  float Number, Number1, Number2;

  /* Initialize values */
  MaxLine = 200;
  TextLine = malloc(sizeof(char) * MaxLine);
  LineMarker = malloc(sizeof(char) * MaxLine);
  NewLineMarker = malloc(sizeof(char) * MaxLine);

  /* Open file for reading.  Make sure file exists. */
  if ((FormatFile = fopen(FileName, "r")) == NULL)
    {
      fprintf(stderr,"get_hinfo:  can't open %s\n", FileName);
      exit(1);
    }

  /* Get XYZ to printer RGB information */
  while((fgets(TextLine, MaxLine, FormatFile) != NULL) && (strstr(TextLine, "#END") == NULL))
	
    {
      if ((strstr(TextLine,"#BEGIN")) != NULL)
	{
	  Index = 0;
	  while (Index < 4)
	    {
	    
	      fgets(TextLine, MaxLine, FormatFile);

	      /* LineMarker indicates where = first occurs in TextLine.
	       * If there is no = sign on this line, configuration file 
	       * is wrong
	       */
	      if ((LineMarker = strchr(TextLine,'=')) == NULL)
		{
		  fprintf(stderr, "Invalid configuration file for %s\n", FileName);
		  exit(1);		    
		}
	    
 	      /* get_number will continuously return the first number on
	       * the line, and update LineMarker to point to the line position
	       * after the first number has been removed from the line.  The
	       * 4th line of numbers that we get correspond to the XYZ values
	       * for the white point.
	       */

	      Number = get_number(LineMarker, NewLineMarker);
	      if (Index == 3)
		WhiteXYZ[0] = Number;
	      else
		RConv[Index] = Number;
	      LineMarker=NewLineMarker;
	      Number = get_number(LineMarker, NewLineMarker);
	      if (Index == 3)
		WhiteXYZ[1] = Number;
	      else
		GConv[Index] = Number;
	      LineMarker=NewLineMarker;
	      Number = get_number(LineMarker, NewLineMarker);
	      if (Index == 3)
		WhiteXYZ[2] = Number;
	      else
		BConv[Index] = Number;  

	     
	      Index++;	      
	    }
	}
    }   


  /* Now get DPI information */
  Index = 0;
  while((fgets(TextLine, MaxLine, FormatFile) != NULL) && (Index < 2))
    {
      /* LineMarker indicates where = first occurs in TextLine.
       * If there is no = sign on this line, configuration file 
       * is wrong
       */
      if ((LineMarker = strchr(TextLine,'=')) == NULL)
	{
	  fprintf(stderr, "Invalid configuration file for %s\n", FileName);
	  exit(1);		    
	}

      if (strstr(TextLine,"Grey") != NULL)
	Mode = GREY;
      else if (strstr(TextLine,"Color") != NULL)
	Mode = COLOR;
      else
	{
	  fprintf(stderr, "Invalid printer information\n");
	  exit(1);
	}

      /* get_number will continuously return the first number on
       * the line, and update LineMarker to point to the line position
       * after the first number has been removed from the line.
       */
      
      Number1 = get_number(LineMarker, NewLineMarker);
      LineMarker=NewLineMarker;
      Number2 = get_number(LineMarker, NewLineMarker);

      switch (Mode)
	{
	case COLOR:
	  {
	    ColorDPI[0] = Number1;
	    ColorDPI[1] = Number2;
	    break;
	  }
	case GREY:
	  {
	    GreyDPI[0] = Number1;
	    GreyDPI[1] = Number2;
	    break;
	  }
	}

      Index++;

    }      
 
  fclose(FormatFile);
  free(TextLine);
  free(LineMarker);


}


/*******************************************************************
 * get_hinfo                                                       *
 * =========                                                       *
 * Extracts the information needed to transform CMYK to a          *
 * calibrated CMYK for the particular halftone pattern specified.  *
 * The function returns a 1D array, which contains 4 elements:     *
 * The first element corresponds conversions for C, the second     *
 * for M, the third for Y, the last for K.                         *
 *******************************************************************/ 
void get_hinfo(FileName, Conv)
char *FileName;
float Conv[4];
{
  FILE *FormatFile;
  char *TextLine, *LineMarker, *NewLineMarker;
  int MaxLine, Index;
  float Number;


  /* Initialize values */
  MaxLine = 200;
  TextLine = malloc(sizeof(char) * MaxLine);
  NewLineMarker = malloc(sizeof(char) * MaxLine);

  /* Open file for reading.  Make sure file exists. */
  if ((FormatFile = fopen(FileName, "r")) == NULL)
    {
      fprintf(stderr, "get_hinfo:  can't open %s\n", FileName);
      exit(1);
    }

  while((fgets(TextLine, MaxLine, FormatFile) != NULL) && (strstr(TextLine, "#END") == NULL))
	
    {


      /* After getting the line #BEGIN, start filling in arrays
       * with transformation factors
       */
      if ((strstr(TextLine,"#BEGIN")) != NULL) 
	{
	  for (Index = 0; Index < 4; Index++)
	    {
	    
	      fgets(TextLine, MaxLine, FormatFile);
 	      /* LineMarker indicates where = first occurs in TextLine.
	       * If there is no = sign on this line, configuration file 
	       * is wrong
	       */
	      if ((LineMarker = strchr(TextLine,'=')) == NULL)
		{
		  fprintf(stderr, "Invalid configuration file for %s\n", FileName);
		  exit(1);		    
		}	      


 	      /* get_number will continuously return the first number on
	       * the line, and update LineMarker to point to the line position
	       * after the first number has been removed from the line.
	       */
	      Number = get_number(LineMarker, NewLineMarker);
	      Conv[Index] = Number;		     
	      LineMarker = NewLineMarker;

	    }
	}
    }
  fclose(FormatFile);
  free(TextLine);
  free(NewLineMarker);
}




/*******************************************************************
 * rgb2xyz                                                         *
 * =======                                                         *
 * Takes 3 arrays representing R, G, and B and transforms them to  *
 * 3 new arrays representing X, Y, Z.  The transformation is based *
 * on the monitor calibration, since we assume that we want to     *
 * match the printout to what appears on the monitor.  We assume   *
 * linear monitor RGB values.  Transformation factors are taken    *
 * from the Rendering.m tutorial (= XYZ'*phosphors).               *
 *******************************************************************/ 
void rgb2xyz(R, G, B, X, Y, Z, ImgH, ImgW)
unsigned char R[], G[], B[];
float X[], Y[], Z[];
int ImgH, ImgW;
{
  int Row, Col, Index;
  float ScaledR, ScaledG, ScaledB;

  for (Row = 0; Row < ImgH; Row++)
    {
      for (Col = 0; Col < ImgW; Col++)
	{
	  Index = Row * ImgW + Col;
	 
	  /* Because program reads in RGB values that run from 0 to 255,
	   * but all conversion matrices assume RGB values from 0 to 1,
	   * program needs to use scaled RGB values.
	   */
	  ScaledR = R[Index] / 255.0;
	  ScaledG = G[Index] / 255.0;
	  ScaledB = B[Index] / 255.0;

	  X[Index] = 25.9232 * ScaledR + 36.136 * ScaledG + 22.9809 * ScaledB;
	  Y[Index] = 14.6733 * ScaledR + 69.9554 * ScaledG + 11.458 * ScaledB;
	  Z[Index] = 1.3834 * ScaledR + 12.3235 * ScaledG + 119.3425 * ScaledB;
	}
    }
}


/*******************************************************************
 * xyz2cmyk                                                        *
 * ========                                                        *
 * Takes 3 arrays representing X, Y, and Z and transforms them     *
 * into 4 new arrays representing Cyan, Magenta, Yellow, Black.    *
 * Because CMYK is a subtractive space, the program actually       *
 * converts XYZ to printer-specific RGB, then converts that to     *
 * CMYK by the formulae C=1-R, M=1-G, Y=1-B.  For now, don't use K.*
 * The transformation is printer-specific and that information is  *
 * stored in a file based on the printer name.                     *
 *******************************************************************/ 
void xyz2cmyk(X, Y, Z, Cyan, Magenta, Yellow, Black, ImgH, ImgW, Printer, GreyDPI, ColorDPI)
float X[], Y[], Z[];
float Cyan[], Magenta[], Yellow[], Black[];
int GreyDPI[], ColorDPI[];
int ImgH, ImgW;
char *Printer;
{
  int Row, Col, Index;
  float RConv[3], GConv[3], BConv[3], WhiteXYZ[3];
  float ScaledZ, ScaledX, ScaledY;
 
  get_pinfo(Printer, RConv, GConv, BConv, GreyDPI, ColorDPI, WhiteXYZ);

  /* printouts for testing purposes only */
  /*  printf("Printer Info\n");
   *printf("RConv: %f %f %f,  GConv: %f %f %f, BConv: %f %f %f\n",RConv[0],RConv[1],RConv[2],GConv[0],GConv[1],GConv[2],BConv[0],BConv[1],BConv[2]);
   *printf("DPI: Grey = %d, %d, Color = %d, %d\n", GreyDPI[0], GreyDPI[1], ColorDPI[0], ColorDPI[1]);
   *printf("Whitepoint: X= %f, Y=%f, Z=%f\n",WhiteXYZ[0], WhiteXYZ[1], WhiteXYZ[2]);
   */

  for (Row = 0; Row < ImgH; Row++)
    {
      for (Col = 0; Col < ImgW; Col++)
	{
	  Index = Row * ImgW + Col;

	  /* Scale XYZ so that white looks white from printer */
	  ScaledX = X[Index] * (WhiteXYZ[0] / 85.0402);
	  ScaledY = Y[Index] * (WhiteXYZ[1] / 96.0867);
	  ScaledZ = Z[Index] * (WhiteXYZ[2] / 133.0494);
	   

	  Cyan[Index] = (WhiteXYZ[0] -  ScaledX) * RConv[0]  + (WhiteXYZ[1] - ScaledY) * RConv[1] + (WhiteXYZ[2] - ScaledZ) * RConv[2];
	  Magenta[Index] = (WhiteXYZ[0] -  ScaledX) * GConv[0]  + (WhiteXYZ[1] - ScaledY) * GConv[1] + (WhiteXYZ[2] - ScaledZ) * GConv[2];
	  Yellow[Index] = (WhiteXYZ[0] -  ScaledX) * BConv[0]  + (WhiteXYZ[1] - ScaledY) * BConv[1] + (WhiteXYZ[2] - ScaledZ) * BConv[2];
	  Black[Index] = 0x00;


	  if (Cyan[Index] > 1)
	    Cyan[Index] = 1;
	  else if (Cyan[Index] < 0)
	    Cyan[Index] = 0;
	   
	  if (Magenta[Index] > 1)
	    Magenta[Index] = 1;
	  else if (Magenta[Index] < 0)
	    Magenta[Index] = 0;
	   
	  if (Yellow[Index] > 1)
	    Yellow[Index] = 1;
	  else if (Yellow[Index] < 0)
	    Yellow[Index] = 0;
	  
	  /*  printf("XYZ: %f %f %f %f   \n",X[Index],Y[Index],Z[Index],ScaledZ);
	   *printf("CMY: %f %f %f    \n",Cyan[Index],Magenta[Index],Yellow[Index]); 
	   */	  
	}
    }

}



/*******************************************************************
 * cmyk_calib                                                      *
 * ==========                                                      *
 * Takes CMYK data and calibrates it based on the halftoning       *
 * pattern being used.  Function outputs a new set of CMYK         *
 * data to be output to the printer.  Parameters associated with   *
 * the halftone pattern/calibration transformation are stored in   *
 * a file.                                                         *
 *******************************************************************/ 
void cmyk_calib(C, M, Y, K, NewC, NewM, NewY, NewK, ImgH, ImgW, Halftone)
unsigned char NewC[], NewM[], NewY[], NewK[];
float C[], M[], Y[], K[];
int ImgH, ImgW;
char *Halftone;
{
  int Row, Col, Index;
  float Conv[4];

  get_hinfo(Halftone, Conv);

  if (strcmp(Halftone,"errdiffuse") == 0)
    {      
      for (Row = 0; Row < ImgH; Row++)
	{
	  for (Col = 0; Col < ImgW; Col++)
	    {
	      Index = Row * ImgW + Col;
	      
	      NewC[Index] = (unsigned char)(255 * asin(C[Index]) * 2 / PI );  
	      NewM[Index] = (unsigned char)(255 * pow(asin(M[Index]) * 2 / PI,-0.7));  
	      NewY[Index] = (unsigned char)(255 * (-0.93199 + Y[Index]) / .1005);  
	      NewK[Index] = (unsigned char)(255 * asin(K[Index]) * 2 / PI );
	
	    }
	}
    }
  else
    {
      for (Row = 0; Row < ImgH; Row++)
	{
	  for (Col = 0; Col < ImgW; Col++)
	    {
	      Index = Row * ImgW + Col;
	  
	      NewC[Index] = (unsigned char)(255 * pow(C[Index],Conv[0]));  
	      NewM[Index] = (unsigned char)(255 * pow(M[Index],Conv[1]));  
	      NewY[Index] = (unsigned char)(255 * pow(Y[Index],Conv[2]));  
	      NewK[Index] = (unsigned char)(255 * pow(K[Index],Conv[3]));  	      
	    }
	}
    }


}



/*******************************************************************
 * extract_rgb                                                     *
 * ===========                                                     *
 * Take the image, which is a string of characters--each character *
 * represents repeated sequence of alpha, red, green, blue.        *
 * Generate 3 one-dimensional arrays, representing the red, green  *
 * and blue components of the image.                               *
 *******************************************************************/ 

void extract_rgb(Img, ImgH, ImgW, ImgR, ImgG, ImgB)
int ImgH, ImgW;
unsigned char Img[];
unsigned char ImgR[];
unsigned char ImgG[];
unsigned char ImgB[];
{
  int row, col;
 
  for(row=0; row < ImgH; row++)
    {
      for(col=0; col < ImgW; col++)
	{
	  ImgR[row*ImgW + col] = Img[4*row*ImgW + 4*col + 1];
	  ImgG[row*ImgW + col] = Img[4*row*ImgW + 4*col + 2];
	  ImgB[row*ImgW + col] = Img[4*row*ImgW + 4*col + 3];	  
	}
    }

}

/*******************************************************************
 * sample                                                          *
 * ======                                                          * 
 * Takes an image and produces a new, smaller image that has       *
 * a width and height that is a multiple of 8 pixels.  If the      *
 * desired size of the new, smaller image is not a multiple of 8,  *
 * it is padded with white space.                                  *
 *******************************************************************/
void sample(Img, ImgH, ImgW, NewImgH, NewImgW, PadH, PadW, NewImg)
unsigned char Img[];
unsigned char NewImg[];
int ImgW, ImgH, NewImgH, NewImgW, PadH, PadW;
{
  float SampleIncW, SampleIncH, TrueSampleCol, TrueSampleRow;
  int Row, Col, SampleCol, SampleRow;
  int TrueImgW, TrueImgH;

  /* Used to determine what pixel value in original image
   * corresponds to given pixel in new, sampled image.
   */
  
  SampleIncW = ((float)ImgW) / NewImgW;
  SampleIncH = ((float)ImgH) / NewImgH;

 /* Actual image size depends on sampled image size + padding needed */
  TrueImgW = NewImgW + PadW;
  TrueImgH = NewImgH + PadH;


  /* Sample rows and columns */
  SampleRow = 0;
  TrueSampleRow = 0;
  for (Row = 0; Row < NewImgH; Row++)
    {


      /* Make sure that last pixel in column of new image is same
       * as last pixel in column of original image
       */
      if (Row == (NewImgH - 1))
	SampleRow = ImgH - 1;

      /* Get values from original image except for last pixel in row*/
      SampleCol = 0;
      TrueSampleCol = 0;
      for (Col = 0; Col < (NewImgW - 1); Col++)
	{
	 
	  NewImg[Row * TrueImgW + Col] = Img[SampleRow * ImgW + SampleCol];
	 
	  TrueSampleCol = TrueSampleCol + SampleIncW;
	  SampleCol = floor(TrueSampleCol +  0.5);  /* Need to add
						     * 0.5 to simulate
					     	     * rounding to the
					       	     * nearest integer
					       	     */
	}

      /* Make sure that last pixel in row of new image is same
       * as last pixel in row of original image
       */
      NewImg[Row * TrueImgW + Col] = Img[SampleRow * ImgW + (ImgW - 1)];


      /* Pad image with white pixels to make image width a 
       * multiple of 8 pixels 
       */

      for (Col = 0; Col < PadW; Col++)
	{
	  NewImg[Row * TrueImgW + (Col + NewImgW)] = 0xff;
       	}
       
      TrueSampleRow = TrueSampleRow + SampleIncH;
      SampleRow = floor(TrueSampleRow + 0.5);
     
    }




  /* Pad image rows with white pixels to make image height a 
   * multiple of 8 pixels
   */

  for (Row = 0; Row < PadH; Row++)
    {
      for (Col = 0; Col < (NewImgW + PadW); Col++)
       {
	 NewImg[(Row + NewImgH) * TrueImgW + Col] = 0xff;
       }
    }
   
}

/*******************************************************************
 * resize_avg                                                      *
 * ==========                                                      *
 * Algorithm to resize image using an averaging algorithm, which   *
 * works by expanding the image and then sampling it:              *
 *        1.  Across a given row, interpolate color values for     *
 *            pixels.  Interpolation is linear.  Repeat for rows   *
 *            for which pixel values are known.                    *
 *        2.  Using same interpolation scheme, interpolate values  *
 *            across a given column.  Repeat for all columns.      *
 *        3.  Special case for last pixel in a row/column of the   *
 *            original image.  Set that last pixel to the last     *
 *            pixel in the row/column of the expanded image and    *
 *            then interpolate.  This will cause some distortion   *
 *            since the left and bottom edges of the image will    *
 *            be interpolated over more pixels than the rest of    *
 *            the expanded image, but hopefully this will not be   *
 *            that noticeable to the viewer.                       *
 *        4.  Call the function sample.                            *
 *        5.  Notes:                                               *
 *                The upsampling/downsampling process uses         *
 *                the closest rational fraction.  Some error       *
 *                may occur here.                                  *  
 *******************************************************************/


void resize_avg(Img, OrigImgH, OrigImgW, NewImgH, NewImgW, PadH, PadW, NewImg)
unsigned char Img[];
unsigned char NewImg[];
int OrigImgW, OrigImgH, NewImgH, NewImgW, PadH, PadW;
{
   int ExpRatioW, ExpRatioH, ExpImgW, ExpImgH;
   int NumXtraPixelsW, NumXtraPixelsH;
   float Ratio, ColorIncB, ColorIncT, ColorIncH, ColorT, ColorB;
   int Row, Col, CountW, CountH;
   unsigned char TLColor, TRColor;  /* Top left, top right colors */
   unsigned char BLColor, BRColor;  /* Bottom left & right colors */
   unsigned char Color;   
   unsigned char *ExpandImg;
   int i,j;

   Ratio = ((float) NewImgW)/OrigImgW;
   ExpRatioW = ceil(Ratio);    /* Round ratio up to next greatest integer */
   Ratio = ((float) NewImgH)/OrigImgH;
   ExpRatioH = ceil(Ratio);
   ExpImgW = ExpRatioW * OrigImgW;
   ExpImgH = ExpRatioH * OrigImgH;
  
   ExpandImg = malloc(sizeof(char) * ExpImgW * ExpImgH);

   /* Expand image columns and rows */
   for(Row=0; Row < (OrigImgH - 1); Row++)
     {
       for (Col=0; Col < (OrigImgW - 1); Col++)
	 {
	   /* Get 4 pixel values for colors to interpolate between */
	   TLColor = Img[Row * OrigImgW + Col];
	   TRColor = Img[Row * OrigImgW + Col + 1];
	   BLColor = Img[(Row + 1) * OrigImgW + Col];
	   BRColor = Img[(Row + 1) * OrigImgW + Col + 1];

	   /* Calculate the incremental difference to add to each
	    * pixel for the top and bottom.  Special case exists
            * for handling the next-to-last column of the
	    * original image.
	    */

	   if ((Col == (OrigImgW - 2)) && (ExpRatioW != 1))
	     {
	       Ratio = (float) 1.5 * ExpRatioW;
	       NumXtraPixelsW = ceil(Ratio);
	     }
	   else
	     {
	       NumXtraPixelsW = ExpRatioW;
	     }

	       
	   ColorIncT = (float) (TRColor - TLColor) / NumXtraPixelsW;
	   ColorIncB = (float) (BRColor - BLColor) / NumXtraPixelsW;

	   for (CountW=0; CountW <= NumXtraPixelsW; CountW++)
	     {

	       /* Interpolate along a column.  Special case
                * for handling the last row.
		*/
	       
	       if ((Row == (OrigImgH - 2)) && (ExpRatioH != 1))
		 {
		   Ratio = (float) 1.5 * ExpRatioH;
		   NumXtraPixelsH = ceil(Ratio);
		 }
	       else
		 {
		   NumXtraPixelsH = ExpRatioH;
		 }

	       ColorT = TLColor + CountW * ColorIncT;
	       ColorB = BLColor + CountW * ColorIncB;
	       ColorIncH = (float) (ColorB - ColorT) / NumXtraPixelsH;

	       for (CountH=0; CountH <= NumXtraPixelsH; CountH++)
		 {
		   ExpandImg[(Row * ExpRatioH  + CountH) * ExpImgW  + (Col * ExpRatioW + CountW)] = (char) (ColorT + CountH * ColorIncH);
		 }
	     
	     }
	     
	 }

     }

   /* Call sampling/padding routine */
   sample(ExpandImg, ExpImgH, ExpImgW, NewImgH, NewImgW, PadH, PadW, NewImg);

   /* Free up memory */
   free(ExpandImg);

}   

/*******************************************************************
 * resize_repeat                                                   *
 * =============                                                   *
 * Algorithm to resize image using "repeat pixel" algorithm, which *
 * works as follows:                                               *
 *        1.  Since non-integer resizing is allowed, first extract *
 *            out by what amount image needs to be expanded and    *
 *            then sampled.                                        *
 *        2.  For expansion:  replicate pixel columns by           *
 *            ExpRatioW and then replicate rows by ExpRatioH.      *
 *        3.  Sampling:  Extract out pixels.                       *
 *        4.  Return the final image as a 1D array                 *
 *        5.  Notes:                                               *
 *                The upsampling/downsampling process uses         *
 *                the closest rational fraction.  Some error       *
 *                may occur here.                                  *  
 *******************************************************************/


void resize_repeat(Img, OrigImgH, OrigImgW, NewImgH, NewImgW, PadH, PadW, NewImg)
unsigned char Img[];
unsigned char NewImg[];
int OrigImgW, OrigImgH, NewImgH, NewImgW, PadH, PadW;

{
   int ExpRatioW, ExpRatioH, ExpImgW, ExpImgH;
   float Ratio;
   int Row, Col, RepeatW, RepeatH;
   unsigned char *ExpandImg;
   int i,j;

   Ratio = ((float) NewImgW)/OrigImgW;
   ExpRatioW = ceil(Ratio);    /* Round ratio up to next greatest integer */
   Ratio = ((float) NewImgH)/OrigImgH;
   ExpRatioH = ceil(Ratio);
   ExpImgW = ExpRatioW * OrigImgW;
   ExpImgH = ExpRatioH * OrigImgH;
   ExpandImg = malloc(sizeof(char) * ExpImgW * ExpImgH);

   /* Expand image columns and rows*/
   for(Row=0; Row < OrigImgH; Row++)
     {
       for (Col=0; Col < OrigImgW; Col++)
	 {
	   for (RepeatW=0; RepeatW < ExpRatioW; RepeatW++)
	     {
	       for (RepeatH=0; RepeatH < ExpRatioH; RepeatH++)
		 {
		   ExpandImg[(Row * ExpRatioH  + RepeatH) * ExpImgW  + (Col * ExpRatioW + RepeatW)] = Img[Row * OrigImgW + Col];
		 }
	     
	     }
	     
	 }

     }



   /* Call sampling/padding routine */
   sample(ExpandImg, ExpImgH, ExpImgW, NewImgH, NewImgW, PadH, PadW, NewImg);

   /* Free up memory */
   free(ExpandImg);
}   



/*******************************************************************
 * main                                                            *
 * ====                                                            *
 * Formats arguments and calls subroutines                         *
 *******************************************************************/

  
main(Argc, Argv)
int Argc;
char *Argv[];

{
  int OrigImgH, OrigImgW, NewImgH, NewImgW;
  int PaddedH, PaddedW, PaddingForH, PaddingForW;
  char *Printer, *Interp, *Halftone;
  unsigned char *ImgR, *ImgG, *ImgB;
  unsigned char *NewImgR, *NewImgG, *NewImgB;
  unsigned char *NewC, *NewM, *NewY, *NewK;
  float *Cyan, *Magenta, *Yellow, *Black;
  float *X, *Y, *Z;
  int Algorithm, i, j, Index;
  int GreyDPI[2], ColorDPI[2];

  if (Argc != 6) 
    {
      printf("%d   ",Argc);
      fprintf(stderr, "Wrong number of arguments\n");
      exit(1);
    }
  
  /* Assign variables to arguments passed to function */
  NewImgW = atoi(Argv[1]);
  NewImgH = atoi(Argv[2]);
  Printer = Argv[3];
  Halftone = Argv[4];
  Interp = Argv[5];

  /* Check that arguments for printer, interp are valid.
   * Are assuming the image sizes are valid
   */

  if ((strcmp(Printer, "ise1600cm") != 0) && (strcmp(Printer, "ise755cm") != 0))
    {
      fprintf(stderr, "Invalid printer name\n");
      exit(1);
    }


  if (strcmp(Interp,"repeat") == 0) 
    Algorithm = REPEAT;
  else if (strcmp(Interp, "avg") == 0) 
    Algorithm = AVG;
  else 
  {
      fprintf(stderr, "Invalid interpolation algorithm\n");
      exit(1);
  }

  /* Resized images need to be multiples of 8 for compression/
   * halftoning part of process
   */
  PaddingForH = 8 - (NewImgH % 8);
  PaddingForW = 8 - (NewImgW % 8);
   
  if (PaddingForH == 8)
    PaddingForH = 0;

  if (PaddingForW == 8)
    PaddingForW = 0;

  PaddedH = NewImgH + PaddingForH;
  PaddedW = NewImgW + PaddingForW;

  /* Get original image width and height data.  This should be the
   * first 2 integers from the image input file.
   */
  scanf("%d %d %d ", &OrigImgW, &OrigImgH, &i);
 

  /* Allocate space for images */
  ImgR=malloc(sizeof(char) * OrigImgH * OrigImgW); 
  ImgG=malloc(sizeof(char) * OrigImgH * OrigImgW);
  ImgB=malloc(sizeof(char) * OrigImgH * OrigImgW);
  NewImgR=malloc(sizeof(char) * PaddedH * PaddedW);
  NewImgG=malloc(sizeof(char) * PaddedH * PaddedW);
  NewImgB=malloc(sizeof(char) * PaddedH * PaddedW);

  /* Get image data*/
  for(i=0; i < OrigImgH; i++)
    {
      for(j=0; j < OrigImgW; j++)
	{
	  getchar();   /* This is the alpha value.  Ignore this */
	  ImgR[i * OrigImgW + j] = getchar();
	  ImgG[i * OrigImgW + j] = getchar();
	  ImgB[i * OrigImgW + j] = getchar();
	}
    }

  
  /* extract_rgb(Img, OrigImgH, OrigImgW, ImgR, ImgG, ImgB); */

  /* Printing for testing purposes only */ 
  /* printf("RGB Image\n");
   *for(i=0; i < OrigImgH; i++)
   * {
   *  for(j=0; j < OrigImgW; j++)
   *{
   * printf("R:  %c, G:  %c, B:  %c\n",ImgR[i * OrigImgW + j], ImgG[i * OrigImgW + j], ImgB[i * OrigImgW + j]);
   *}
   *}
   */


  
  /* Depending on what algorithm is desired, call different
   * functions for resizing the rgb arrays
   */
  switch(Algorithm)
    {
    case 0:
      {
	resize_repeat(ImgR, OrigImgH, OrigImgW, NewImgH, NewImgW, PaddingForH, PaddingForW, NewImgR);
	resize_repeat(ImgG, OrigImgH, OrigImgW, NewImgH, NewImgW, PaddingForH, PaddingForW, NewImgG);
	resize_repeat(ImgB, OrigImgH, OrigImgW, NewImgH, NewImgW, PaddingForH, PaddingForW, NewImgB);
	break;
      }
    case 1:
      {
	resize_avg(ImgR, OrigImgH, OrigImgW, NewImgH, NewImgW, PaddingForH, PaddingForW, NewImgR);
	resize_avg(ImgG, OrigImgH, OrigImgW, NewImgH, NewImgW, PaddingForH, PaddingForW, NewImgG);
	resize_avg(ImgB, OrigImgH, OrigImgW, NewImgH, NewImgW, PaddingForH, PaddingForW, NewImgB);
	break;
      }
    }
  


  /* Free up memory */
  free(ImgR);
  free(ImgG);
  free(ImgB);


  /* Convert RGB values of new image to XYZ values */
  X=malloc(sizeof(float) * PaddedH * PaddedW);
  Y=malloc(sizeof(float) * PaddedH * PaddedW);
  Z=malloc(sizeof(float) * PaddedH * PaddedW);
  rgb2xyz(NewImgR, NewImgG, NewImgB, X, Y, Z, PaddedH, PaddedW);
  free(NewImgR);
  free(NewImgG);
  free(NewImgB);


  /* Convert XYZ values to CMYK for specific printer */
  Cyan=malloc(sizeof(float) * PaddedH * PaddedW);
  Magenta=malloc(sizeof(float) * PaddedH * PaddedW);
  Yellow=malloc(sizeof(float) * PaddedH * PaddedW);
  Black=malloc(sizeof(float) * PaddedH * PaddedW);
  xyz2cmyk(X, Y, Z, Cyan, Magenta, Yellow, Black, PaddedH, PaddedW, Printer, GreyDPI, ColorDPI);
  free(X);
  free(Y);
  free(Z);


  /* Convert CMYK values to calibrated ones for specific halftone */
  NewC=malloc(sizeof(char) * PaddedH * PaddedW);
  NewM=malloc(sizeof(char) * PaddedH * PaddedW);
  NewY=malloc(sizeof(char) * PaddedH * PaddedW);
  NewK=malloc(sizeof(char) * PaddedH * PaddedW); 
  cmyk_calib(Cyan, Magenta, Yellow, Black, NewC, NewM, NewY, NewK, PaddedH, PaddedW, Halftone); 
  free(Cyan);
  free(Magenta);
  free(Yellow);
  free(Black);

  /* Printing for testing purposes only */
  /*Index = 0;
   *for(i=0; i < PaddedH; i++)
   * {
   *  for(j=0; j < PaddedW; j++)
   *{
   *  Index = i*PaddedW + j;
   *  printf(" %x  %x  %x  %x  \n",NewC[Index], NewM[Index], NewY[Index], NewK[Index]);	 
   *}
   * }
   */


  /* Output data to standard output to be used in next part of process */
  Index = 0;
  printf("%d %d\n",PaddedW, PaddedH);
  for(i=0; i < PaddedH; i++)
    {
      for(j=0; j < PaddedW; j++)
	{
	  Index = i*PaddedW + j;
	  putchar(NewC[Index]);
	  putchar(NewM[Index]);
	  putchar(NewY[Index]);
	  putchar(NewK[Index]);	 
	}
    }

  /* Free up memory */
  free(NewC);
  free(NewM);
  free(NewY);
  free(NewK);
}

previous home