import java.awt.*;
import javax.swing.*;

import java.awt.event.*;

// Anzeige zweier identischer Bilder
// Alle Änderungen werden im rechten Fenster geändert
// Funktionen:

// Links:  Änderungen der Farbe (RGB bis Graustufen)
// Rechts: Verschiedenartige Filter

// mit dem Schalter load, kann man beliebige Bilder laden
// Startpfad ist c:\pictures 

import java.awt.geom.*;

import java.io.*;
import java.awt.image.*;

import java.awt.color.*;

import com.sun.image.codec.jpeg.*;


// für Opendialog
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.filechooser.*;
import java.util.Hashtable;
import java.util.Enumeration;


public class Dialog_Picture2_2D extends JFrame {


  JLabel LFarbe = new JLabel("Farbpalette");
  JComboBox imgTypeCombo;

  JLabel LFilter = new JLabel("Filter");
  JComboBox FilterCombo;

  JButton BnDraw= new JButton("Draw");

  JButton BnOpenPicture= new JButton("Load");

  CCanvas2 _canvasL = new CCanvas2(this, "grundfarben.jpg"); // bld.jpg"); //Stonehenge.jpg");
  CCanvas2 _canvasR = new CCanvas2(this, "grundfarben.jpg"); // bld.jpg"); //Stonehenge.jpg");


  public Dialog_Picture2_2D(String caption) {
    setTitle(caption);
    setGUI();
  }


  // Eintragen der GUI-Elemente
  private void setGUI() {
    this.getContentPane().setLayout( new GridBagLayout() );

    JPanel pnTop = new JPanel();
    pnTop.setLayout( new GridBagLayout() );

    this.getContentPane().add(pnTop,
        new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0
            ,GridBagConstraints.WEST,
            GridBagConstraints.HORIZONTAL,
            new Insets(20,20,20,20), 0, 0));

  // -----------------------------

    pnTop.add(LFarbe,
        new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST,
            GridBagConstraints.NONE,
            new Insets(0,10,20,20), 0, 0));

    imgTypeCombo = new JComboBox();
    pnTop.add(imgTypeCombo,
        new GridBagConstraints(1, 0, 1, 1, 100.0, 0.0
            ,GridBagConstraints.WEST,
            GridBagConstraints.HORIZONTAL,
            new Insets(0,0,20,50), 0, 0));

     // Da TYPE_INT_RGB den Wert 1 hat , wird einfach eine eins dazu addiert
     // Beim auslösen.  Farbfilter := getIndex() +1 
    imgTypeCombo.addItem("INT_RGB");  //TYPE_INT_RGB=1
    imgTypeCombo.addItem("INT_ARGB");
    imgTypeCombo.addItem("INT_ARGB_PRE");
    imgTypeCombo.addItem("INT_BGR");
    imgTypeCombo.addItem("3BYTE_BGR");
    imgTypeCombo.addItem("4BYTE_ABGR");
    imgTypeCombo.addItem("4BYTE_ABGR_PRE");
    imgTypeCombo.addItem("USHORT_565_RGB");
    imgTypeCombo.addItem("USHORT_555_RGB");
    imgTypeCombo.addItem("BYTE_GRAY");
    imgTypeCombo.addItem("USHORT_GRAY");
    // imgTypeCombo.addItem("BYTE_BINARY"); //TYPE_BYTE_BINARY=12
    imgTypeCombo.setSelectedIndex(0);

  // -------------------------------------------

    pnTop.add(LFilter,
        new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST,
            GridBagConstraints.NONE,
            new Insets(0,10,20,20), 0, 0));

   setComboBox();
    pnTop.add(FilterCombo,
        new GridBagConstraints(3, 0, 1, 1, 100.0, 0.0
            ,GridBagConstraints.WEST,
            GridBagConstraints.HORIZONTAL,
            new Insets(0,0,20,20), 0, 0));

   // ------------------------------
    pnTop.add(BnDraw,
        new GridBagConstraints(4, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST,
            GridBagConstraints.NONE,
            new Insets(0,20,20,20), 0, 0));

    pnTop.add(BnOpenPicture,
        new GridBagConstraints(5, 0, 1, 1, 0.0, 0.0
            ,GridBagConstraints.WEST,
            GridBagConstraints.NONE,
            new Insets(0,20,20,20), 0, 0));



     // Ereignisse definieren
    BnDraw.addActionListener(new java.awt.event.ActionListener() {

      public void actionPerformed(ActionEvent e) {
        BnDraw_Click();
      }
    });

     // Ereignisse definieren
    BnOpenPicture.addActionListener(new java.awt.event.ActionListener() {

      public void actionPerformed(ActionEvent e) {
        BnOpenPicture_Click();
      }
    });


    this.getContentPane().add(_canvasL,
        new GridBagConstraints(0, 1, 1, 1, 100.0, 100.0
            ,GridBagConstraints.WEST,
            GridBagConstraints.BOTH,
            new Insets(0,20,20,20), 0, 0));
    this.getContentPane().add(_canvasR,
        new GridBagConstraints(1, 1, 1, 1, 100.0, 100.0
            ,GridBagConstraints.WEST,
            GridBagConstraints.BOTH,
            new Insets(0,20,20,20), 0, 0));

  }


  // laden eines neuen Bildes
  private void BnOpenPicture_Click() {
   JFileChooser jFileChooser1 = new JFileChooser();
   String sFileName;

   MyFileFilter2 pictFilter;

   pictFilter = new MyFileFilter2( "jpg", "Bilder");

   jFileChooser1.setMultiSelectionEnabled(false);
   jFileChooser1.setCurrentDirectory( new File("D:\\hs-harz\\Vorlesungen\\java\\Grafik2D\\") );
   jFileChooser1.addChoosableFileFilter(pictFilter);

   if (JFileChooser.APPROVE_OPTION == jFileChooser1.showOpenDialog(this)) {
      sFileName = jFileChooser1.getSelectedFile().getPath();
      _canvasL.setPicture(sFileName);
      _canvasR.setPicture(sFileName);
      _canvasR.updateUI();
      _canvasL.updateUI();
    }
  }


  private void BnDraw_Click() {
    CItem item = (CItem) FilterCombo.getSelectedItem();
    _canvasR.imageType = imgTypeCombo.getSelectedIndex()+1;
    _canvasR.op = item.op;
    _canvasR.updateUI();

    _canvasL.updateUI();
  }



  private ConvolveOp ConvertMatrixFilter(float[] Filter) {
    Kernel kernel;
    kernel = new Kernel(3,3,Filter );
    ConvolveOp cop = new ConvolveOp(kernel,
                                 ConvolveOp.EDGE_NO_OP,
                                 null);
    return cop;
  }

   // erzeugt alle Operatoren und trägt diese in die ComboBox ein
   // zentrale Stelle fuer Aenderungen
  private void setComboBox() {
   BufferedImageOp op;
   FilterCombo = new JComboBox();

   // 1. HauptTyp:  Filter, ein einfache Feld mit 9 Elementen
   // jeweils neun Pixel werden dazu berechnet
   float[] IdentityFilter =
                           {0.0f, 0.0f, 0.0f,
                            0.0f, 1.0f, 0.0f,
                            0.0f, 0.0f, 0.0f
                            };
   FilterCombo.addItem( new CItem("Kein Filter", ConvertMatrixFilter(IdentityFilter)) );

   float[] LowPassFilter =
                           {0.1f, 0.1f, 0.1f,
                            0.1f, 0.2f, 0.1f,
                            0.1f, 0.1f, 0.1f
                            };
   FilterCombo.addItem( new CItem("Low Pass Filter", ConvertMatrixFilter(LowPassFilter)) );

   float[] EdgePassFilter =
                           {0.0f, -1.0f,  0.0f,
                           -1.0f,  4.0f, -1.0f,
                            0.0f, -1.0f,  0.0f
                           };
   FilterCombo.addItem( new CItem("Edge Filter", ConvertMatrixFilter(EdgePassFilter)) );

   float[] HighPassFilter =
                           {0.0f, -1.0f,  0.0f,
                           -1.0f,  5.0f, -1.0f,
                            0.0f, -1.0f,  0.0f
                           };
   FilterCombo.addItem( new CItem("High Pass Filter", ConvertMatrixFilter(HighPassFilter)) );

   float[] BlurFilter =
                           {1.0f/9.0f, 1.0f/9.0f,  1.0f/9.0f,
                            1.0f/9.0f, 1.0f/9.0f,  1.0f/9.0f,
                            1.0f/9.0f, 1.0f/9.0f,  1.0f/9.0f
                           };
   FilterCombo.addItem( new CItem("Blur Filter", ConvertMatrixFilter(BlurFilter)) );


   // ---------------------------------------------
   // 2. HauptTyp:  Algorithmen, jeweils 256 Werte
   //               Gilt für alle Farben !!
   //               verwendet wird die Klasse LookupOp

   short[] brighten = new short[256];
   short[] betterBrighten1 = new short[256];
   short[] betterBrighten2 = new short[256];
   short[] posterize = new short[256];
   short[] invert = new short[256];
   short[] straight = new short[256];
   short[] zero = new short[256];

   for (int i=0; i<256; i++) {
     brighten[i] = (short) (128+i/2);  // Lineare Aufhellung

     // quadratische Aufhellung, am Anfang wenig
     betterBrighten1[i] = (short) (Math.sqrt((double)i / 255.0)*255.0);
     betterBrighten2[i] = (short) (i*1.2); // etwas mehr, kann Überlauf geben
     if (betterBrighten2[i]>255)  betterBrighten2[i]=255;
     posterize[i] = (short) (i - (i%32)); // modulo
     invert[i] = (short) (255-i);  // oder !
     straight[i] = (short) (i);
     zero[i] = (short) (0);
   }


                                           // 0 ist Offset
   FilterCombo.addItem( new CItem("Brighten",
          new LookupOp(new ShortLookupTable(0,brighten),null)) );


   FilterCombo.addItem( new CItem("Better Brighten1",
          new LookupOp(new ShortLookupTable(0,betterBrighten1),null)) );

   FilterCombo.addItem( new CItem("Better Brighten2",
          new LookupOp(new ShortLookupTable(0,betterBrighten2),null)) );

   FilterCombo.addItem( new CItem("Posterize",
          new LookupOp(new ShortLookupTable(0,posterize),null)) );

   FilterCombo.addItem( new CItem("Invert",
          new LookupOp(new ShortLookupTable(0,invert),null)) );


   // ---------------------------------------------
   // 3. HauptTyp:  FarbFilter, aus jeweils drei Feldern
   //               verwendet wird die Klasse LookupOp

   short[][] readOnly = { invert, straight, straight};
   short[][] greenOnly = { straight, invert, straight};
   short[][] blueOnly = { straight, straight, invert};

   FilterCombo.addItem( new CItem("Red Invert",
          new LookupOp(new ShortLookupTable(0,readOnly),null)) );
   FilterCombo.addItem( new CItem("Green Invert",
          new LookupOp(new ShortLookupTable(0,greenOnly),null)) );
   FilterCombo.addItem( new CItem("Blue Invert",
          new LookupOp(new ShortLookupTable(0,blueOnly),null)) );


   short[][] readRemove = { zero, straight, straight};
   short[][] greenRemove = { straight, zero, straight};
   short[][] blueRemove = { straight, straight, zero};

   FilterCombo.addItem( new CItem("Red Remove",
          new LookupOp(new ShortLookupTable(0,readRemove),null)) );
   FilterCombo.addItem( new CItem("Green Remove",
          new LookupOp(new ShortLookupTable(0,greenRemove),null)) );
   FilterCombo.addItem( new CItem("Blue Remove",
          new LookupOp(new ShortLookupTable(0,blueRemove),null)) );


   FilterCombo.addItem( new CItem("GrayScale",
          new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY),null)) );


   // ---------------------------------------------
   // 4. HauptTyp:  RescaleOp
   //               definiert wird eine lineare Funktion y=a*x + b
   //               a und b werden übergeben
   //               Die Funktion wird für jeden Pixel angewandt
   /* for each pixel from Source object {
        for each band/component of the pixel {
            dstElement = (srcElement*scaleFactor) + offset
        }
    }
   */

   FilterCombo.addItem( new CItem("Rescale 0.5, 0 (dunkler)",
          new RescaleOp(0.5f, 0,null)) );
   FilterCombo.addItem( new CItem("Rescale 0.5, 64 (dunkler)",
          new RescaleOp(0.5f, 64,null)) );
   FilterCombo.addItem( new CItem("Rescale 0.5, 128",
          new RescaleOp(0.5f, 128,null)) );
   FilterCombo.addItem( new CItem("Rescale 1.2, 0 (heller)",
          new RescaleOp(1.2f, 0,null)) );
   FilterCombo.addItem( new CItem("Rescale 1.5, 0 (heller)",
          new RescaleOp(1.5f, 0,null)) );

   // Frage für Studenten, welche Farbe ergibt sich ?
   FilterCombo.addItem( new CItem("Rescale 0.0, 255",
          new RescaleOp(0.0f, 255,null)) );

   FilterCombo.setSelectedIndex(0);



  } // setComboBox() 



    // dient zum Beenden
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if(e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }



    public static void main(String args[]) {
      Dialog_Picture2_2D f = new Dialog_Picture2_2D("Dialog_Picture2_2D");
     	f.setSize(900,700);
    	f.setVisible(true);
    }


}


class CCanvas2 extends JPanel {

  private Component cmp;
  private BufferedImage image;


  public BufferedImageOp op=null;
  public int imageType=BufferedImage.TYPE_INT_RGB; // 1

  public CCanvas2(Component cmp, String sFilename) {
    this.cmp = cmp;
    image = loadPicture(sFilename);
  }

  public void setPicture(String sFilename) {
    image = loadPicture(sFilename);
  }

  private BufferedImage loadPicture(String filename)  {
    BufferedImage image=null;
    try {
     	FileInputStream in = new FileInputStream(filename);
      JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(in);
      image = decoder.decodeAsBufferedImage();
      in.close();
    }
    catch (IOException e) {
    }
    return image;
  }

  public void paint (Graphics g) {
    // holt die Abmessungen des Canvas
    Rectangle r = new Rectangle();  // double wert
    g.getClipBounds(r);
        // Löschen des Bildschirms
    g.setColor(Color.white);
    g.fillRect(0,0, (int)r.getWidth(), (int)r.getHeight() );

    Graphics2D g2 = (Graphics2D) g;


    if (op==null) {
      g2.drawImage(image,0,0, Color.red, cmp);
    }
    else {
      int iw = image.getWidth(this);
      int ih = image.getHeight(this);
      int x = 0;
      int y = 0;
      int w = (int)r.getWidth();
      int h = (int)r.getHeight();

      BufferedImageOp biop = null;
      BufferedImage bimg = new BufferedImage(iw,ih,imageType);
                                 //BufferedImage.TYPE_INT_RGB);

      // Dieser Op-Filter ist eine allgemeine Klasse
      // wird durch wechseln der ComboBox übergeben                            
      op.filter(image,bimg);

      g2.drawImage(bimg,0,0, Color.blue, cmp);

    }

  }

}




class CItem {

  public String sItem;
  public BufferedImageOp op; //LookupOp op;

  public CItem(String sItem) {
    this.sItem = sItem;
    this.op = null;
  }
  public CItem(String sItem, BufferedImageOp op) {
    this.sItem = sItem;
    this.op = op;
  }
 /*public CItem(String sItem, LookupOp op) {
    this.sItem = (BandCombineOp)sItem;
    this.op = op;
  }
   */
  public String toString() {
    return sItem;
  }

}




class MyFileFilter2 extends javax.swing.filechooser.FileFilter {

    private static String TYPE_UNKNOWN = "Type Unknown";
    private static String HIDDEN_FILE = "Hidden File";

    private Hashtable filters = null;
    private String description = null;
    private String fullDescription = null;
    private boolean useExtensionsInDescription = true;

    /**
     * Creates a file filter. If no filters are added, then all
     * files are accepted.
     *
     * @see #addExtension
     */
    public MyFileFilter2() {
     	this((String) null, (String) null);
    }

    /**
     * Creates a file filter that accepts files with the given extension.
     * Example: new ExampleFileFilter("jpg");
     *
     * @see #addExtension
     */
    public MyFileFilter2(String extension) {
     	this(extension,null);
    }

    /**
     * Creates a file filter that accepts the given file type.
     * Example: new ExampleFileFilter("jpg", "JPEG Image Images");
     *
     * Note that the "." before the extension is not needed. If
     * provided, it will be ignored.
     *
     * @see #addExtension
     */
    public MyFileFilter2(String extension, String description) {
     	this(new String[] {extension}, description);
    }

    /**
     * Creates a file filter from the given string array.
     * Example: new ExampleFileFilter(String {"gif", "jpg"});
     *
     * Note that the "." before the extension is not needed adn
     * will be ignored.
     *
     * @see #addExtension
     */
    public MyFileFilter2(String[] filters) {
     	this(filters, null);
    }

    /**
     * Creates a file filter from the given string array and description.
     * Example: new ExampleFileFilter(String {"gif", "jpg"}, "Gif and JPG Images");
     *
     * Note that the "." before the extension is not needed and will be ignored.
     *
     * @see #addExtension
     */
    public MyFileFilter2(String[] filters, String description) {
      this.filters = new Hashtable(filters.length);
      for (int i = 0; i < filters.length; i++) {
          // add filters one by one
          addExtension(filters[i]);
      }
      setDescription(description);
    }

    /**
     * Return true if this file should be shown in the directory pane,
     * false if it shouldn't.
     *
     * Files that begin with "." are ignored.
     *
     * @see #getExtension
     * @see FileFilter#accepts
     */
    public boolean accept(File f) {
      if(f != null) {
          if(f.isDirectory()) {
        return true;
          }
          String extension = getExtension(f);
          if(extension != null && filters.get(getExtension(f)) != null) {
        return true;
          };
      }
      return false;
    }

    /**
     * Return the extension portion of the file's name .
     *
     * @see #getExtension
     * @see FileFilter#accept
     */
     public String getExtension(File f) {
        if(f != null) {
            String filename = f.getName();
            int i = filename.lastIndexOf('.');
            if(i>0 && i<filename.length()-1) {
          return filename.substring(i+1).toLowerCase();
            };
        }
        return null;
    }

    /**
     * Adds a filetype "dot" extension to filter against.
     *
     * For example: the following code will create a filter that filters
     * out all files except those that end in ".jpg" and ".tif":
     *
     *   ExampleFileFilter filter = new ExampleFileFilter();
     *   filter.addExtension("jpg");
     *   filter.addExtension("tif");
     *
     * Note that the "." before the extension is not needed and will be ignored.
     */
    public void addExtension(String extension) {
      if(filters == null) {
          filters = new Hashtable(5);
      }
      filters.put(extension.toLowerCase(), this);
      fullDescription = null;
    }


    /**
     * Returns the human readable description of this filter. For
     * example: "JPEG and GIF Image Files (*.jpg, *.gif)"
     *
     * @see setDescription
     * @see setExtensionListInDescription
     * @see isExtensionListInDescription
     * @see FileFilter#getDescription
     */
    public String getDescription() {
      if(fullDescription == null) {
          if(description == null || isExtensionListInDescription()) {
        if(description != null) {
            fullDescription = description;
        }
        fullDescription += " (";
        // build the description from the extension list
        Enumeration extensions = filters.keys();
        if(extensions != null) {
            fullDescription += "." + (String) extensions.nextElement();
            while (extensions.hasMoreElements()) {
          fullDescription += ", " + (String) extensions.nextElement();
            }
        }
        fullDescription += ")";
          } else {
        fullDescription = description;
          }
      }
      return fullDescription;
    }

    /**
     * Sets the human readable description of this filter. For
     * example: filter.setDescription("Gif and JPG Images");
     *
     * @see setDescription
     * @see setExtensionListInDescription
     * @see isExtensionListInDescription
     */
    public void setDescription(String description) {
   	  this.description = description;
    	fullDescription = null;
    }

    /**
     * Determines whether the extension list (.jpg, .gif, etc) should
     * show up in the human readable description.
     *
     * Only relevent if a description was provided in the constructor
     * or using setDescription();
     *
     * @see getDescription
     * @see setDescription
     * @see isExtensionListInDescription
     */
    public void setExtensionListInDescription(boolean b) {
      useExtensionsInDescription = b;
      fullDescription = null;
    }

    /**
     * Returns whether the extension list (.jpg, .gif, etc) should
     * show up in the human readable description.
     *
     * Only relevent if a description was provided in the constructor
     * or using setDescription();
     *
     * @see getDescription
     * @see setDescription
     * @see setExtensionListInDescription
     */
    public boolean isExtensionListInDescription() {
     	return useExtensionsInDescription;
    }
}  // MyFilter

