/***************************************************************************

Program to demonstrate alpha blending

***************************************************************************/

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import com.sun.image.codec.jpeg.*;

public class alpha extends JFrame {

   // This frame will hold the primary components:
   // 	An object to hold the buffered image and its associated operations
   //	Components to control the interface

   // Instance variables
   private BufferedImage image;   // the image
   private MyImageObj view;       // a component in which to display an image
   private JLabel infoLabel;      // an informative label for the simple GUI
   private JButton OriginalButton;// Button to restore original image
   private int x, y;              // Store x, y mouse position for paint
   private JLabel alphaLabel;   // Label for threshold slider
   private JSlider alphaslider;

   // Constructor for the frame
   public alpha () {

      super();				// call JFrame constructor

      this.buildMenus();		// helper method to build menus
      this.buildComponents();		// helper method to set up components
      this.buildDisplay();		// Lay out the components on the display
   }

   //  Builds the menus to be attached to this JFrame object
   //  Primary side effect:  menus are added via the setJMenuBar call
   //  		Action listeners for the menu items are anonymous inner
   //		classes here
   //  This helper method is called once by the constructor

   private void buildMenus () {

      final JFileChooser fc = new JFileChooser(".");
      JMenuBar bar = new JMenuBar();
      this.setJMenuBar (bar);
      JMenu fileMenu = new JMenu ("File");
      JMenuItem fileopen = new JMenuItem ("Open");
      JMenuItem fileexit = new JMenuItem ("Exit");

      fileopen.addActionListener(
	 new ActionListener () {
             public void actionPerformed (ActionEvent e) {
                int returnVal = fc.showOpenDialog(alpha.this);
                if (returnVal == JFileChooser.APPROVE_OPTION) {
                   File file = fc.getSelectedFile();
                   image = readImage(fc.getCurrentDirectory() + 
                           "\\" + fc.getDescription(file));
		   view.setImage(image);
		   view.showImage();
                }
             }
         }
      );
      fileexit.addActionListener(
	 new ActionListener () {
             public void actionPerformed (ActionEvent e) {
                   System.exit(0);
             }
         }
      );

      fileMenu.add(fileopen);
      fileMenu.add(fileexit);
      bar.add(fileMenu);
   }

   //  Allocate components (these are all instance vars of this frame object)
   //  and set up action listeners for each of them 
   //  This is called once by the constructor

   private void buildComponents() {

      // Create components to in which to display image and GUI controls
      //view = new alpha(readImage("boat.gif"));
      view = new MyImageObj();
      infoLabel = new JLabel("Original Image");
      OriginalButton = new JButton("Original");

      alphaLabel = new JLabel("Alpha Value: " + Double.toString(127.0 / 255.0));
      alphaslider = new JSlider( SwingConstants.VERTICAL, 0, 255, 10);
      alphaslider.setMajorTickSpacing(10);
      alphaslider.setPaintTicks(true);

      // slider event triggers a display of thresholded image
      alphaslider.addChangeListener(
         new ChangeListener() {
	    public void stateChanged (ChangeEvent e) {
	       view.alphaImage(alphaslider.getValue());
               infoLabel.setText("Alpha Blend Image");
	       alphaLabel.setText("Alpha Value: " +
                       Double.toString((float) alphaslider.getValue() / 255.0));
            }
         }
      );

      // Button listeners activate the buffered image object in order
      // to display appropriate function
      OriginalButton.addActionListener(
	 new ActionListener () {
             public void actionPerformed (ActionEvent e) {
                view.showImage();
                infoLabel.setText("Original");
             }
         }
      );
   }

   // This helper method adds all components to the content pane of the
   // JFrame object.  Specific layout of components is controlled here

   private void buildDisplay () {

      // Build first JPanel
      JPanel controlPanel = new JPanel();
      GridLayout grid = new GridLayout (1, 5);
      controlPanel.setLayout(grid);
      controlPanel.add(infoLabel);
      controlPanel.add(OriginalButton);

      // Build second JPanel
      JPanel thresholdcontrolPanel = new JPanel();
      BorderLayout layout = new BorderLayout (5, 5);
      thresholdcontrolPanel.setLayout (layout);
      thresholdcontrolPanel.add(alphaLabel,BorderLayout.NORTH);
      thresholdcontrolPanel.add(alphaslider,BorderLayout.CENTER);

      // Add panels and image data component to the JFrame
      Container c = this.getContentPane();
      c.add(view, BorderLayout.EAST);
      c.add(controlPanel, BorderLayout.SOUTH);
      c.add(thresholdcontrolPanel, BorderLayout.WEST);

   }

   // This method reads an Image object from a file indicated by
   // the string provided as the parameter.  The image is converted
   // here to a BufferedImage object, and that new object is the returned
   // value of this method.
   // The mediatracker in this method can throw an exception

   public BufferedImage readImage (String file) {

      Image image = Toolkit.getDefaultToolkit().getImage(file);
      MediaTracker tracker = new MediaTracker (new Component () {});
      tracker.addImage(image, 0);
      try { tracker.waitForID (0); }
      catch (InterruptedException e) {}
         BufferedImage bim = new BufferedImage 
             (image.getWidth(this), image.getHeight(this), 
             BufferedImage.TYPE_INT_RGB);
      Graphics2D big = bim.createGraphics();
      big.drawImage (image, 0, 0, this);
      return bim;
   }

   // The main method allocates the "window" and makes it visible.
   // The windowclosing event is handled by an anonymous inner (adapter)
   // class
   // Command line arguments are ignored.

   public static void main(String[] argv) {

      JFrame frame = new alpha();
      frame.pack();
      frame.setVisible(true);
      frame.addWindowListener (
	 new WindowAdapter () {
	    public void windowClosing ( WindowEvent e) {
		System.exit(0);
	    }
         }
      );
   }

   /*****************************************************************

   This is a helper object, which could reside in its own file, that 
   extends JLabel so that it can hold a BufferedImage

   I've added the ability to apply image processing operators to the
   image and display the result

   ***************************************************************************/

   public class MyImageObj extends JLabel {

      // instance variable to hold the buffered image
      private BufferedImage bim=null;
      private BufferedImage filteredbim=null;

      //  tell the paintcomponent method what to draw 
      private boolean showfiltered=false;

      // Default constructor
      public MyImageObj() {
      }

      // This constructor stores a buffered image passed in as a parameter
      public MyImageObj(BufferedImage img) {
	 bim = img;
         filteredbim = new BufferedImage 
            (bim.getWidth(), bim.getHeight(), BufferedImage.TYPE_INT_RGB);
         setPreferredSize(new Dimension(bim.getWidth(), bim.getHeight()));
      }

      // This mutator changes the image by resetting what is stored
      // The input parameter img is the new image;  it gets stored as an
      //     instance variable
      public void setImage(BufferedImage img) {
         if (img == null) return;
         bim = img;
         filteredbim = new BufferedImage 
            (bim.getWidth(), bim.getHeight(), BufferedImage.TYPE_INT_RGB);
         setPreferredSize(new Dimension(bim.getWidth(), bim.getHeight()));
	 showfiltered=false;
         this.repaint();
      }

      // accessor to get a handle to the bufferedimage object stored here
      public BufferedImage getImage() {
         return bim;
      }

      //  apply alpha blend
      //  input parameter is an integer indicating alpha level
      public void alphaImage(int value) {
         if (bim == null) return;

	//  Create graphics context for filtered buffered image
	Graphics2D g2 = filteredbim.createGraphics();
	g2.setColor(new Color(255,255,255));
	g2.fillRect(0, 0, filteredbim.getWidth(), filteredbim.getHeight());

	//  Create alpha composite object
	AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) value / (float) 255.0);
	g2.setComposite(ac);
	g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
		RenderingHints.VALUE_ANTIALIAS_ON);
	g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
		RenderingHints.VALUE_INTERPOLATION_BICUBIC);

	// g2.clip(destPath);
	// g2.setTransform(af);

	g2.drawImage(bim, 0, 0, null);
	g2.dispose();

	showfiltered=true;
	this.repaint();
      }

      //  show current image by a scheduled call to paint()
      public void showImage() {
         if (bim == null) return;
	 showfiltered=false;
	 this.repaint();
      }

      //  get a graphics context and show either filtered image or
      //  regular image
      public void paintComponent(Graphics g) {
	 Graphics2D big = (Graphics2D) g;
	 if (showfiltered)
            big.drawImage(filteredbim, 0, 0, this);
         else
            big.drawImage(bim, 0, 0, this);
      }
   }

}
