import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.geom.*;

public class ImageFilters extends JPanel
{
	JMenuItem invertItem, bwItem, rotateItem;
	JFrame f;
	Image img, imgToRotate;
	BufferedImage offimg;
	Graphics2D offscreen;
	int imgPixel[]=new int [300*300];
	int currentAngle = 5;
	RotateFrame rotateFrame;
	boolean sprayOn = false;
	int mouseX, mouseY;
	
	public ImageFilters()
	{
		offimg = new BufferedImage(300,300, BufferedImage.TYPE_INT_RGB);
		offscreen = offimg.createGraphics();
		img = Toolkit.getDefaultToolkit().getImage("salva.jpg");
		MediaTracker tracker = new MediaTracker(this);
		tracker.addImage(img, 0);
		try { tracker.waitForID(0); } 
		catch (InterruptedException exception) {}		
		offscreen.drawImage(img,0,0,300,300,null);
		
		invertItem = new JMenuItem("Inverti");
		bwItem = new JMenuItem("Bianco e nero");
		rotateItem = new JMenuItem("Ruota...");
		invertItem.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){invertImage();}});
		bwItem.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){blackWhiteImage();}});
		rotateItem.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){rotateFrame.showRotateFrame();}});
		JMenu menu = new JMenu("Filtri");
		menu.add(invertItem);
		menu.add(bwItem);
		menu.add(rotateItem);
		JMenuBar menuBar = new JMenuBar();
		menuBar.add(menu);
		f = new JFrame(); 
		f.setSize(300,300);
		f.setJMenuBar(menuBar);
		f.setTitle("Bouncing");
		f.setLocation(400,300);
		f.add(this);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		addMouseMotionListener( new MouseMotionAdapter(){ public void mouseDragged(MouseEvent e){
			mouseX = e.getX();
			mouseY = e.getY();
			}});
			
		addMouseListener( new MouseAdapter(){ public void mousePressed(MouseEvent e){
			mouseX = e.getX();
			mouseY = e.getY();
			sprayOn = true;
			convoluzione();
			rotateFrame.setVisible(false);
			} public void mouseReleased(MouseEvent e){ sprayOn = false;} });
		rotateFrame = new RotateFrame(this);
	}
	
	public void convoluzione()
	{
		// APPLICAZIONE DEL FILTRO DI CONVOLUZIONE ####################################################################################
		offscreen.drawImage(offimg,0,0,300,300,null);
		float [] data={		1.0f/9f, 1.0f/9f, 1.0f/9f, 
											1.0f/9f, 1.0f/9f, 1.0f/9f, 
											1.0f/9f, 1.0f/9f, 1.0f/9f };
//		float [] data={		0.0f, -1.0f, 0.0f, 
//											-1.0f, 5.0f, -1.0f, 
//											0.0f, -1.0f, 0.0f };
		Kernel kernel= new Kernel(3,3,data);
		// Il parametro ConvolveOp.EDGE_NO_OP definito nella funzione ConvolveOp effettua la convoluzione dei bordi dell'immagine ricopiando i valori delle righe e delle colonne adiacenti per completare la maschera convolutiva
		ConvolveOp  op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
		BufferedImage tmpImgBuffered = new BufferedImage(offimg.getWidth(null),offimg.getHeight(null), offimg.getType());
		op.filter(offimg, tmpImgBuffered);
		offscreen.drawImage(tmpImgBuffered,0,0,null);
		// ###########################################################################################################################
	}
	
	public void sprayzza()
	{
		offscreen.setColor(Color.red);
		int x = 25 - (int)(Math.random()*50);
		int y = 25 - (int)(Math.random()*50);
		offscreen.fillOval(mouseX + (x*x*x)/400 - 2, mouseY + (y*y*y)/400 - 2, 4,4);
	}
	
	public void rotateImage()
	{
		// ROTAZIONE DI IMMAGINI #####################################################################################################		
		AffineTransform origXform = offscreen.getTransform();
		AffineTransform newXform = (AffineTransform)(origXform.clone());
		newXform.rotate(Math.toRadians(currentAngle), 150, 150);
		offscreen.setTransform(newXform);
		offscreen.fillRect(-100, -100, 500,500);
		offscreen.drawImage(imgToRotate, 0, 0, 300,300, null);
		offscreen.setTransform(origXform);
		// ###########################################################################################################################
	}
	
	public void invertImage()
	{
		// FILTRI SUI SINGOLI PIXEL #############################################################################
		PixelGrabber pg = new PixelGrabber(offimg,0,0,300,300,imgPixel,0,300);
		try { pg.grabPixels(); }	catch (InterruptedException err){ System.out.println(err);}
		for (int k = 0; k < imgPixel.length; k++)
		{
			// FILTRO NEGATIVO ################################################################################
			Color newColor = new Color(imgPixel[k]);
			Color bw = new Color(255-newColor.getRed(), 255-newColor.getGreen(), 255-newColor.getBlue());
			imgPixel[k] = bw.getRGB();
		}
		Image tmpImg = createImage(new MemoryImageSource(300,300,ColorModel.getRGBdefault(),imgPixel,0,300));
		offscreen.drawImage(tmpImg,0,0,null);
	}
	public void blackWhiteImage()
	{
		// FILTRI SUI SINGOLI PIXEL #############################################################################
		PixelGrabber pg = new PixelGrabber(offimg,0,0,300,300,imgPixel,0,300);
		try { pg.grabPixels(); }	catch (InterruptedException err){ System.out.println(err);}
		for (int k = 0; k < imgPixel.length; k++)
		{
			// FILTRO NEGATIVO ################################################################################
			Color newColor = new Color(imgPixel[k]);
			Color bw = new Color(newColor.getRed(), newColor.getRed(), newColor.getRed());
			imgPixel[k] = bw.getRGB();
		}
		Image tmpImg = createImage(new MemoryImageSource(300,300,ColorModel.getRGBdefault(),imgPixel,0,300));
		offscreen.drawImage(tmpImg,0,0,null);
	}
	
	public void paint(Graphics g)
	{
		super.paintComponent(g);
		g.drawImage(offimg, 0,0,null);
		if (sprayOn) sprayzza();
		repaint();
		try { Thread.sleep(10); } catch(Exception e){}
	}
	
	public static void main(String[] a)
	{
		ImageFilters b = new ImageFilters();
	}
}

class RotateFrame extends JFrame implements ChangeListener
{
	JSlider rotateValue;
	ImageFilters parent;
	public RotateFrame(ImageFilters parent)
	{
		setTitle("Rotate image");
		setSize(300,50);
		this.parent = parent;
		rotateValue = new JSlider(-360,360);
		rotateValue.addChangeListener(this);
		getContentPane().add(rotateValue);
	}
	public void stateChanged(ChangeEvent e)
	{
		parent.currentAngle = rotateValue.getValue();
		parent.rotateImage();
		parent.repaint();
	}
	public void showRotateFrame()
	{
		// CATTURO L'IMMAGINE IN UN'IMMAGINE TEMPORANEA CHE USO PER RUOTARLA --------------------------------
		PixelGrabber pg = new PixelGrabber(parent.offimg,0,0,300,300,parent.imgPixel,0,300);
		try { pg.grabPixels(); }	catch (InterruptedException err){ System.out.println(err);}
		parent.imgToRotate = createImage(new MemoryImageSource(300,300,ColorModel.getRGBdefault(),parent.imgPixel,0,300));
		// --------------------------------------------------------------------------------------------------
		rotateValue.setValue(0);
		Point parentLocation = parent.getLocationOnScreen();
		setLocation(parentLocation.x-4, parentLocation.y - 95);
		setVisible(true);
	}
}
