/*
 * Paraselene
 * Copyright (c) 2009, 2010  Akira Terasaki
 * このファイルは同梱されているLicense.txtに定めた条件に同意できる場合にのみ
 * 利用可能です。
 */
package paraselene;

/**
 * 輝度と色差での色表現。{@link Color}クラスへの入出力として使用可能です。<br>
 * 人間の目に敏感な輝度に最も多くの情報量を持たせ、色の変化を表す情報量が減ります。
 * JPEG や MPEG のように人間の目が鈍感な要素を欠損させて圧縮をかける
 * フォーマットで利用されています。<br>
 * YUV は Color より表現可能範囲が広いため、Colorクラスへ設定する際に
 * YUV の設定値が Color 空間外であると、Colorに収まるように丸め込まれます。
 * このため、YUV を Colorクラスへsetして、その直後getしても同一の値が
 * 返るとは限りません。
 */
public class YUV {
	/**
	 * 輝度。値の範囲は符号なし8ビットです。<br>
	 * 0が黒、255が白です。
	 */
	public int y;
	/**
	 * 青の色差。値の範囲は符号あり8ビットです。<br>
	 * 0が無彩色。0から外れるほど青みを増します。<br>
	 */
	public int u;
	/**
	 * 赤の色差。値の範囲は符号あり8ビットです。<br>
	 * 0が無彩色。0から外れるほど赤みを増します。<br>
	 */
	public int v;

	/**
	 * コンストラクタ。
	 * @param y 輝度。
	 * @param u 赤の色差。
	 * @param v 青の色差。
	 */
	public YUV( int y, int u, int v ) {
		this.y = unsigned_fix( y );
		this.u = signed_fix( u );
		this.v = signed_fix( v );
	}

	private static int unsigned_fix( int v ) {
		if ( v < 0 )	return 0;
		if ( v > 255 )	return 255;
		return v;
	}

	private static int signed_fix( int v ) {
		if ( v < -128 )	return -128;
		if ( v > 127 )	return 127;
		return v;
	}

	static YUV getYUV( Color c ) {
		double[]	s = new double[] { c.getRed(), c.getGreen(), c.getBlue() };
		double[]	d = new double[] {
			0.299 * s[0] + 0.587 * s[1] + 0.114 * s[2],
			-0.169 * s[0] - 0.331 * s[1] + 0.5 * s[2],
			0.5 * s[0] - 0.419 * s[1] - 0.081 * s[2]
		};
		return new YUV( (int)d[0], (int)d[1], (int)d[2] );
	}

	void toColor( Color c ) {
		double[]	s = new double[] {
			unsigned_fix( y ) , signed_fix( u ), signed_fix( v )
		};
		double[]	d = new double[] {
			 s[0] + 1.402 * s[2],
			 s[0] - 0.344 * s[1] - 0.714 * s[2],
			 s[0] + 1.772 * s[1]
		};
		c.setColor( unsigned_fix( (int)d[0] ), unsigned_fix( (int)d[1] ), unsigned_fix( (int)d[2] ) );
	}
}

