// Author: Ooi Wei Tsang
// vim:ts=4:sw=4

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class DCTdemo extends JApplet
{
	Point point_;
	BufferedImage bi_;
	BufferedImage block_;
	Rectangle largePixels_[][];
	Rectangle largeDCTs_[][];
	Rectangle zigzagBar_[] = new Rectangle[64];
	Color colors_[][];
	Color dct_[][];
	static final int zz[] = { 
		0,  1,  5,  6,  14, 15, 27, 28,
		2,  4,  7,  13, 16, 26, 29, 42,
		3,  8,  12, 17, 25, 30, 41, 43,
		9,  11, 18, 24, 31, 40, 44, 53,
		10, 19, 23, 32, 39, 45, 52, 54,
		20, 22, 33, 38, 46, 51, 55, 60,
		21, 34, 37, 47, 50, 56, 59, 61,
		35, 36, 48, 49, 57, 58, 62, 63};

	double zigzag_[] = new double[64];

	double cos_table_[][];
	int dctElements[][] = {
		{1,1,1,1,1,1,1,1},
		{1,1,1,1,1,1,1,1},
		{1,1,1,1,1,1,1,1},
		{1,1,1,1,1,1,1,1},
		{1,1,1,1,1,1,1,1},
		{1,1,1,1,1,1,1,1},
		{1,1,1,1,1,1,1,1},
		{1,1,1,1,1,1,1,1},
	};

	static final int w_ = 200;
	static final int h_ = 200;
	static final int origX_ = 10;
	static final int origY_ = 10;
	static final int blockX_ = 220;
	static final int blockY_ = 10;
	static final int dctX_ = 430;
	static final int dctY_ = 10;
	static final int zigzagX_ = 10;
	static final int zigzagY_ = 430;
	static final int zigzagWidth_ = 8;

	public void init()
	{
		setBackground(Color.white);
		initLargePixelsAndColors();
		initDCT();
		bi_ = loadImage();
	}

	private void initDCT()
	{
		cos_table_ = new double[8][8];
		for (int v = 0; v < 8; v++)
			for (int y = 0; y < 8; y++)
				cos_table_[v][y] = Math.cos (
					((double)(2*y+1) * (double)(v) * (Math.PI/16.0)));
	}

	private void initLargePixelsAndColors()
	{
		largePixels_ = new Rectangle[8][8];
		colors_ = new Color[8][8];
		largeDCTs_ = new Rectangle[8][8];
		dct_ = new Color[8][8];
		
		for (int i = 0; i < 8; i++)
			for (int j = 0; j < 8; j++)
			{
				largePixels_[i][j] = new Rectangle(blockX_ + 25*i, blockY_ + 25*j, 25, 25);
				largeDCTs_[i][j] = new Rectangle(dctX_ + 25*i, dctY_ + 25*j, 25, 25);
				colors_[i][j] = Color.green;
				dct_[i][j] = Color.green;
			}
		for (int i = 0; i < 64; i++)
			zigzagBar_[i] = new Rectangle(0,0,0,0);
	}

	private BufferedImage loadImage()
	{
		Image img = getImage(getDocumentBase(), "DCTdemo.jpg");
		try 
		{
			MediaTracker tracker = new MediaTracker(this);
			tracker.addImage(img, 0);
			tracker.waitForID(0);
		}
		catch (Exception e) { }

		int w = img.getWidth(this);
		int h = img.getHeight(this);
		BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d = result.createGraphics();
		g2d.drawImage(img, 0, 0, this);

		// add event listening 
		addMouseMotionListener(new MyMouseAdapter());
		
		return result;
	}

	private void loadBlock(int x, int y)
	{
		for (int i = 0; i < 8; i++)
			for (int j = 0; j < 8; j++)
			{
				colors_[i][j] = new Color(bi_.getRGB(x - origX_ + i, y - origY_ + j));
			}
		calcDCT();
	}

	public void paint(Graphics g)
	{
		Graphics2D g2 = (Graphics2D) g;
		g2.drawImage(bi_,origX_,origY_,this);
		for (int i = 0; i < 8; i++)
			for (int j = 0; j < 8; j++)
			{
				g2.setColor(colors_[i][j]);
				g2.fill(largePixels_[i][j]);
				g2.setColor(dct_[i][j]);
				g2.fill(largeDCTs_[i][j]);
				g2.setColor(Color.green);
				g2.draw(largePixels_[i][j]);
				g2.draw(largeDCTs_[i][j]);
			}

		for (int i = 0; i < 64; i++)
		{
			g2.setColor(Color.white);
			g2.fill(zigzagBar_[i]);

			if (zigzag_[i] > 0) {
				zigzagBar_[i].setLocation(
					(int) Math.round(zigzagX_ + (zigzagWidth_+1)*i - 0.5),
					(int) Math.round(zigzagY_ - zigzag_[i]/8 - 0.5));
			} else {
				zigzagBar_[i].setLocation(
					(int) Math.round(zigzagX_ + (zigzagWidth_+1)*i - 0.5),
					(int) Math.round(zigzagY_ - 0.5));
			}
			zigzagBar_[i].setSize(
				(int) Math.round(zigzagWidth_ - 0.5),
				(int) Math.round(Math.abs(zigzag_[i])/8 - 0.5));
			g2.setColor(new Color(44, 15, 229));
			g2.fill(zigzagBar_[i]);
		}

		g2.setColor(Color.black);
		g2.drawLine(zigzagX_, zigzagY_ - 1, zigzagX_ + (zigzagWidth_+1)*64, zigzagY_-1);
	}

	class MyMouseAdapter extends MouseMotionAdapter
	{
		public void mouseMoved(MouseEvent event) 
		{
			point_ = event.getPoint();
			int x = point_.x;
			int y = point_.y;
			if (x > origX_ && x < origX_ + w_ - 8 && y > origY_ && y < origY_ + h_ - 8)
			{
				loadBlock(x, y);
				repaint();
			}
		}
	}

	void calcDCT()
	{

		int y[][] = new int[8][8];

		for (int i = 0; i < 8; i++)
			for (int j = 0; j < 8; j++)
			{
				y[i][j] = (int)(0.257*colors_[i][j].getRed () +
								0.504*colors_[i][j].getGreen () +
								0.098*colors_[i][j].getBlue () + 16);
			}

		dct(y);

		for (int i = 0; i < 8; i++)
			for (int j = 0; j < 8; j++)
				dct_[i][j] = new Color (y[i][j], y[i][j], y[i][j]);
	}


	void dct (int[][] block)
	{
		double temp[][] = new double[8][8];
		double sum;

		for (int u = 0; u < 8; u++)
			for (int v = 0; v < 8; v++)
			{
				sum = 0.0;
				for (int x = 0; x < 8; x++)
					for (int y = 0; y < 8; y++)	
						sum += block[x][y] * cos_table_[u][x]*cos_table_[v][y];
				temp[u][v] = 0.25 * C(u, v) * sum;
			}

		int zzIndex = 0;
		for (int u = 0; u < 8; u++)
			for (int v = 0; v < 8; v++)
			{
				zigzag_[zz[zzIndex++]] = temp[u][v];
				block[u][v] = (int)(Math.abs(temp[u][v])/8);
				if (block[u][v] < 0)
					block[u][v] = 0;
				if (block[u][v] > 255)
					block[u][v] = 255;
			}
	}

	double C(int u, int v)
	{
		if (u == 0 && v == 0)
			return 0.5;
		else if (u == 0 || v == 0)
			return (1.0 / Math.sqrt (2.0));
		else 
			return 1.0;
	}
}
