
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.net.*;
import java.util.*;
import java.io.*;
import java.lang.Math;

/**
 * Scale is an algorithm to alter the size of an image
 * @author:Craig Strachan after Judy Robertson, SELLIC Online
 * @see code.iface.scale
 */

public class Scale extends Thread {
  
  public Scale() {
  }
  
  private int [] gradeline(int end1, int end2, int scalef) {
    
    /* Given two points 2 * scalef apart in an image, interpolates
     * between those  points and returns the result in an int array
     */
    
    
    int [] result = new int [2 * scalef];
    Color cend1, cend2;
    double red_grad, green_grad, blue_grad;
    int red, green, blue;
    
    
    result[0] = end1;
    result[(2 * scalef) - 1] = end2;
    cend1 = new Color(end1);
    cend2 = new Color(end2);
    if (cend1.getRed() == cend2.getRed()) {
      red_grad = 0.0;
    }
    else if (cend1.getRed() > cend2.getRed()) {
      red_grad = ((cend2.getRed() - cend1.getRed()) + 1) / (2 * (scalef -1));
    }
    else {
      red_grad = ((cend2.getRed() - cend1.getRed()) - 1) / (2 * (scalef - 1));
    }
    
    if (cend1.getGreen() == cend2.getGreen()) {
      green_grad = 0.0;
    }
    else if (cend1.getGreen() > cend2.getGreen()) {
      green_grad = ((cend2.getGreen() - cend1.getGreen()) + 1) / (2 * (scalef -1));
    }
    else {
      green_grad = ((cend2.getGreen() - cend1.getGreen()) - 1) / (2 * (scalef - 1));
    }
    
    if (cend1.getBlue() == cend2.getBlue()) {
      blue_grad = 0.0;
    }
    else if (cend1.getBlue() > cend2.getBlue()) {
      blue_grad = ((cend2.getBlue() - cend1.getBlue()) + 1) / (2 * (scalef -1));
    }
    else {
      blue_grad = ((cend2.getBlue() - cend1.getBlue()) - 1) / (2 * (scalef - 1));
    }
    
    
    for (int i = 1; i < ((2 * scalef) - 2); i++) {
      red = (int) Math.rint ((double) i * red_grad) + cend1.getRed();
      green = (int) Math.rint ((double) i * green_grad) + cend1.getGreen();
      blue = (int) Math.rint ((double) i * blue_grad) + cend1.getBlue();
      Color tmp = new Color(red, green, blue);
      result[i] = tmp.getRGB();
      
    }
    
    return result;
  } /* gradline */
  
  /** 
   *Give an image of width src_width contained in int array src
   *returns the rectangle with top left corner at x,y and with width 
   *width and height height contained in that image as an int array
   *@param src The input image array
   *@param src_width The width of the input image array
   *@param x The x co-ordinate of the top left corner of the rectangle
   *@param y The y co-ordinate of the top left corner of the rectangle
   *@param width The width of the rectangle
   *@param height The height of the rectangle
   *@return The rectangle taken from the image 
   */
  
  public int [] getrect (int [] src, int src_width, int x, int y, int width,
			 int height) {
    
    int [] result = new int [width * height];
    int k = 0;
    for (int j = y; j < y + height; j++) {
      for (int i = x; i < x + width; i++) {
	result [k++] = src[(j * src_width) + i];
      }
    }
    return result;
  } /* getrect */
  
  private int get_point(int x, int y, int width, int [] src_bitmap) {
    /* 
     * returns the value of a int bitmap with width width at point
     * x, y;
     */
    
    return src_bitmap[(y * width) + x];
  } /* get_point */
  
  private void put_point(int point, int x, int y, int width, int [] bitmap) {
    /*
     * Sets point x,y of the bitmap bitmap to have the value of point.
     */
    
    bitmap [(y * width) + x] = point;
  } /* put_point */
  
  
  /**
   *Reduces an image by a factor of scalefactor using the sample method 
   *@param src The input image array
   *@param orig_w The width of the input image
   *@param orig_h The height of the input image
   *@param scalefactor The amount the image is to be shrunk
   *@return The shrunken image array
   */
  
  public int [] shrink_sample (int [] src, int orig_w, int orig_h, int
			       scalefactor) {
    
    /* Reduces an image by a factor of scalefactor using the sample
     * method 
     */
    
    
    int new_w = orig_w / scalefactor;
    int new_h = orig_h / scalefactor;
    int [] result = new int [new_w * new_h];
    int [] sample;
    int sample_point;
    
    for (int i = 0; i < (orig_h - scalefactor); i = i + scalefactor) {
      for (int j = 0; j < (orig_w - scalefactor); j = j + scalefactor) {
	
	sample = getrect (src, orig_w, j, i, scalefactor, scalefactor);
	
	/* sample is simply the top left pixel */
	sample_point = sample[0];
	result [((i / scalefactor) * new_w) + (j / scalefactor)] =
	  sample_point; 
      }
    }
    return result;
  } /* shrink_sample */


  /** 
   *Reduces an image by a factor of scalefactor using the average method 
   *@param src The input image array
   *@param orig_w The width of the input image
   *@param orig_h The height of the input image
   *@param scalefactor The amount the image is to be shrunk
   *@return The shrunken image array
   */

  public int [] shrink_average (int [] src, int orig_w, int orig_h, int
				scalefactor) {
    
    int new_w = orig_w / scalefactor;
    int new_h = orig_h / scalefactor;
    int [] result = new int [new_w * new_h];
    int [] sample;
    int sample_point;
    int red_sum, green_sum, blue_sum;
    Color colorpoint;
    
    if (scalefactor == 1)
    {
        for (int i = 0; i < orig_h*orig_w; i++) {
	    result[i] = src[i];
        }
        return result;
    }
    
    for (int i = 0; i < (orig_h - scalefactor); i = i + scalefactor) {
      for (int j = 0; j < (orig_w - scalefactor); j = j + scalefactor) {
	
	sample = getrect (src, orig_w, j, i, scalefactor, scalefactor);
	
	/* sample_point  is the average of the pixels in the sample */
	red_sum = 0;
	green_sum = 0;
	blue_sum = 0;
	
	for (int k = 0; k < (scalefactor * scalefactor) -1; k ++) {
	  colorpoint = new Color(sample[k]);
	  red_sum += colorpoint.getRed();
	  green_sum += colorpoint .getGreen();
	  blue_sum += colorpoint.getBlue();
	}
	
	Color tmp = new Color ((red_sum / (scalefactor * scalefactor)), 
			       (green_sum / (scalefactor * scalefactor)),
			       (blue_sum / (scalefactor*
					    scalefactor)));
	sample_point = tmp.getRGB();
	result [((i / scalefactor) * new_w) + (j / scalefactor)] =
	  sample_point; 
      }
    }
    
    
    return result;
  } /* shrink_sample */
  
  /** 
   *Grows an image by a factor of scalefactor using the replicate method 
   *@param src The input image array
   *@param orig_w The width of the input image
   *@param orig_h The height of the input image
   *@param scalefactor The amount the image is to be grown
   *@return The grown image array
   */


  public int [] grow_replicate(int [] src, int orig_w, int orig_h,
			       int scalefactor) {
    
    int new_w = orig_w * scalefactor;
    int new_h = orig_h * scalefactor;
    int [] result = new int[new_w * new_h];
    int sample;

    for (int i = 0; i < new_h; i++) {
      for (int j = 0; j < new_w; j++) {
	sample = src[(i / scalefactor) * orig_w + (j / scalefactor)];
	result[i * new_w + j] = sample;
      }
    }
    return result;
  } /* grow_replicate */
	
  /** 
   *Grows an image by a factor of scalefactor using the interpolate method 
   *@param src The input image array
   *@param orig_w The width of the input image
   *@param orig_h The height of the input image
   *@param scalefactor The amount the image is to be grown
   *@return The grown image array
   */

  public int [] grow_interpolate (int [] src, int orig_w, int orig_h,
				  int scalefactor) {
   
    int new_w = orig_w * scalefactor;
    int new_h = orig_h * scalefactor;
    int [] result = new int [new_w * new_h];

    if (scalefactor == 1)
    {
        for (int i = 0; i < orig_h*orig_w; i++) {
	    result[i] = src[i];
        }
        return result;
    }
    
    for (int i = 0; i < (orig_h - 1); i++) {
      for (int j = 0; j < (orig_w -1); j++) {
	int [] line = gradeline(get_point(j, i, orig_w, src),
				get_point(j + 1, i, orig_w, src),
				scalefactor);
	for (int k = 0; k <= scalefactor; k++) {
	  put_point(line[k], (j * scalefactor) + k, i * scalefactor,
		    new_w, result);
	}
	line = gradeline(get_point(j, i, orig_w, src), 
			 get_point(j, i + 1, orig_w, src),
			 scalefactor);
	for (int k = 0; k <= scalefactor; k++) {
	  put_point(line[k], j * scalefactor, (i * scalefactor) + k,
		    new_w, result);
	}
	line = gradeline(get_point(j + 1, i, orig_w, src),
			 get_point(j + 1, i + 1, orig_w, src),
			 scalefactor);
	for (int k = 0; k <= scalefactor; k++) {
	  put_point(line[k], (j + 1) * scalefactor, (i * scalefactor) + k,
		    new_w, result);
	}
	
	for (int k = 1; k <= scalefactor; k++) {
	  line = gradeline(get_point((j * scalefactor), 
				     (i * scalefactor) + k, new_w, result),
			   get_point((j + 1) * scalefactor, 
				     (i * scalefactor) + k, new_w,
				     result), 
			   scalefactor);
	  for (int l = 0; l <= scalefactor; l++) {
	    put_point(line[l], (j * scalefactor) + l, 
		      (i * scalefactor) + k, new_w, result);
	  }
	}
      }
    }
    return result;
  } /* grow_interpolate */
  
  
  
}
