グラデェーション

 スプライン関数を利用して、グラデーションを生成するクラスを作ってみる。
 ■ソースコード
 次のようなコードを書いてみた。
 インターフェイスGradientを定義し、オブジェクトはGradientFactoryで生成する。
 
 ・Gradient.java

package test;

import java.awt.Color;

public interface Gradient {

	public Color getColor(double arg);
	public float[] getColorByFloat(double arg);
	public int getColorByInt(double arg);
}

 ・GradientFactory.java

package test;

import java.awt.Color;

public class GradientFactory {
	public static final Color[] GRADIENT_RGB=new Color[]{Color.BLUE,Color.GREEN,Color.RED};
	public static final Color[] GRADIENT_NATURAL_COLOR=new Color[]{Color.GREEN,new Color(187,135,12),Color.WHITE};

	private GradientFactory(){}
	
	public static Gradient createGradient(Color[] colors){
		if(colors.length==2){
			LinerGradient ret=new LinerGradient(colors,1.0);
			return ret;
		}else if(colors.length>2){
			SplineGradient ret=new SplineGradient(colors,1.0);
			return ret;
		}else{
			throw new IllegalArgumentException();
		}
	}
	
	public static Gradient createGradient(Color[] colors,double order){
		if(colors.length==2){
			LinerGradient ret=new LinerGradient(colors,order);
			return ret;
		}else if(colors.length>2){
			SplineGradient ret=new SplineGradient(colors,order);
			return ret;
		}else{
			throw new IllegalArgumentException();
		}
	}
	
	private static class LinerGradient implements Gradient{
		private double[][] color;
		private double order=1.0;
		
		public LinerGradient(Color[] c,double o){
			color=new double[3][2];
			color[0][0]=(double)c[0].getRed();
			color[0][1]=(double)c[1].getRed();
			color[1][0]=(double)c[0].getGreen();
			color[1][1]=(double)c[1].getGreen();
			color[2][0]=(double)c[0].getBlue();
			color[2][1]=(double)c[1].getBlue();
			order=o;
		}
		
		@Override
		public Color getColor(double arg) {
			double val=Math.pow(arg, order);
			if(val<0)val=0;
			if(val>1.0)val=1.0;
			int r=(int)((color[0][1]-color[0][0])*val+color[0][0]);
			int g=(int)((color[1][1]-color[1][0])*val+color[1][0]);
			int b=(int)((color[2][1]-color[2][0])*val+color[2][0]);
			Color ret=new Color(r,g,b,255);
			return ret;
		}

		@Override
		public float[] getColorByFloat(double arg) {
			double val=Math.pow(arg, order);
			if(val<0)val=0;
			if(val>1.0)val=1.0;
			float r=(float)((color[0][1]-color[0][0])*val+color[0][0])/255.0f;
			float g=(float)((color[1][1]-color[1][0])*val+color[1][0])/255.0f;
			float b=(float)((color[2][1]-color[2][0])*val+color[2][0])/255.0f;
			return new float[]{r,g,b,1.0f};
		}

		@Override
		public int getColorByInt(double arg) {
			double val=Math.pow(arg, order);
			if(val<0)val=0;
			if(val>1.0)val=1.0;
			int r=(int)((color[0][1]-color[0][0])*val+color[0][0]);
			int g=(int)((color[1][1]-color[1][0])*val+color[1][0]);
			int b=(int)((color[2][1]-color[2][0])*val+color[2][0]);
			if(r<0)r=0;
			if(r>255)r=255;
			if(g<0)g=0;
			if(g>255)g=255;
			if(b<0)b=0;
			if(b>255)b=255;
			int ret=(255<<24)+(r<<16)+(g<<8)+b;
			return ret;
		}
	}
	
	private static class SplineGradient implements Gradient{
		private double[][] color;
		private double[] value;
		private Spline[] spline;
		private double order=1.0;
		
		public SplineGradient(Color[] c,double o){
			color=new double[3][c.length];
			value=new double[c.length];
			spline=new Spline[3];
			for(int i=0;i<c.length;i++){
				color[0][i]=(double)c[i].getRed();
				color[1][i]=(double)c[i].getGreen();
				color[2][i]=(double)c[i].getBlue();
				value[i]=(float)i/(float)(color.length-1);
			}
			spline[0]=new Spline(value,color[0]);
			spline[1]=new Spline(value,color[1]);
			spline[2]=new Spline(value,color[2]);
			order=o;
		}
		
		public Color getColor(double v){
			if(Double.isNaN(v)||Double.isInfinite(v)){
				throw new ArithmeticException("NaN");
			}else{
				double val=Math.pow(v, order);
				if(val<0)val=0;
				if(val>1.0)val=1.0;
				int r=(int)(spline[0].interpolate(val));
				int g=(int)(spline[1].interpolate(val));
				int b=(int)(spline[2].interpolate(val));
				if(r<0)r=0;
				if(r>255)r=255;
				if(g<0)g=0;
				if(g>255)g=255;
				if(b<0)b=0;
				if(b>255)b=255;
				Color ret=new Color(r,g,b,255);
				return ret;
			}
		}
		
		public float[] getColorByFloat(double v){
			if(Double.isNaN(v)||Double.isInfinite(v)){
				throw new ArithmeticException("NaN");
			}else{
				double val=Math.pow(v, order);
				if(val<0)val=0;
				if(val>1.0)val=1.0;
				float r=(float)(spline[0].interpolate(val))/255.0f;
				float g=(float)(spline[1].interpolate(val))/255.0f;
				float b=(float)(spline[2].interpolate(val))/255.0f;
				if(r<0.0f)r=0.0f;
				if(r>1.0f)r=1.0f;
				if(g<0.0f)g=0.0f;
				if(g>1.0f)g=1.0f;
				if(b<0.0f)b=0.0f;
				if(b>1.0f)b=1.0f;
				return new float[]{r,g,b,1.0f};
			}
		}

		@Override
		public int getColorByInt(double v) {
			if(Double.isNaN(v)||Double.isInfinite(v)){
				throw new ArithmeticException("NaN");
			}else{
				double val=Math.pow(v, order);
				if(val<0)val=0;
				if(val>1.0)val=1.0;
				int r=(int)(spline[0].interpolate(val));
				int g=(int)(spline[1].interpolate(val));
				int b=(int)(spline[2].interpolate(val));
				if(r<0)r=0;
				if(r>255)r=255;
				if(g<0)g=0;
				if(g>255)g=255;
				if(b<0)b=0;
				if(b>255)b=255;
				int ret=(255<<24)+(r<<16)+(g<<8)+b;
				return ret;
			}
		}
	}
	
	private static class Spline {
		private int num;
		private double[] x, y, z;
		
		public Spline(double[] _x,double[] _y){
			num=_x.length;
			x=_x;
			y=_y;
			spline_NonCycle();
		}
		
		double interpolate(double _x){
			return interpolate_NonCycle(_x);
		}
		
		private void spline_NonCycle(){
			z=new double[num];
			double[] h=new double[num];
			double[] d=new double[num];
			z[0]=z[num-1]=0;
			for(int i=0;i<num-1;i++){
				h[i]=x[i+1]-x[i];
				d[i+1]=(y[i+1]-y[i])/ h[i];
			}
			z[1]=d[2]-d[1]-h[0]*z[0];
			d[1]=2*(x[2]-x[0]);
			for(int i=1;i<num-2;i++){
				double t=h[i]/d[i];
				z[i+1]=d[i+2]-d[i+1]-z[i]*t;
				d[i+1]=2*(x[i+2]-x[i])-h[i]*t;
			}
			z[num-2] -=h[num-2]*z[num-1];
			for(int i=num-2;i>0;i--){
				z[i]=(z[i]-h[i]*z[i+1])/d[i];
			}
		}
		
		private double interpolate_NonCycle(double t){
			int i=0;
			int j=num-1;
			while(i<j){
				int k=(i+j)/2;
				if(x[k]<t){
					i=k+1;
				}else{
					j = k;
				}
			}
			if(i>0)i--;
			double h=x[i+1]-x[i];
			double d=t-x[i];
			return (((z[i+1]-z[i])*d/h+z[i]*3)*d
						+((y[i+1]-y[i])/ h
						-(z[i]*2+z[i+1])*h))*d+y[i];
		}
	}
}

 ・GradientPanel.java(テスト用)

package test;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class GradientPanel extends JPanel {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private enum GradientType{NORTH_SOUTH,WEST_EAST,
							NORTHWEST_SOUTHEAST,
							SOUTHWEST_NORTHEAST,CENTER}
	private int image_width,image_height;
	private BufferedImage image=null;
	private Gradient grad;
	private GradientType obj=GradientType.WEST_EAST;
	
	public GradientPanel(Gradient g,GradientType t) {
		super();
		grad=g;
		obj=t;
	}
	public GradientPanel(boolean arg0,Gradient g,GradientType t) {
		super(arg0);
		grad=g;
		obj=t;
	}
	public GradientPanel(LayoutManager arg0, boolean arg1,Gradient g,GradientType t) {
		super(arg0, arg1);
		grad=g;
		obj=t;
	}
	public GradientPanel(LayoutManager arg0,Gradient g,GradientType t) {
		super(arg0);
		grad=g;
		obj=t;
	}
	
	@Override
	protected void paintComponent(Graphics arg0) {
		super.paintComponent(arg0);
		if(image_width!=getWidth()||image_height!=getHeight()){
			createImage();
		}
		if(image!=null){
			arg0.drawImage(image, 0, 0, this);
		}
	}
	
	private void createImage(){
		image_width=getWidth();
		image_height=getHeight();
		if(image_width==0||image_height==0){
			image=null;
			return;
		}
		image=new BufferedImage(image_width,image_height,BufferedImage.TYPE_INT_ARGB);
		int[][] pixel=new int[image_width][image_height];
		switch(obj){
			case NORTH_SOUTH:
				initPixelsAtNS(pixel);
				break;
			case WEST_EAST:
				initPixelsAtWE(pixel);
				break;
			case NORTHWEST_SOUTHEAST:
				initPixelsAtNWSE(pixel);
				break;
			case SOUTHWEST_NORTHEAST:
				initPixelsAtSWNE(pixel);
				break;
			case CENTER:
				initPixelsAtCC(pixel);
				break;
		
		}
		for(int i=0;i<image_width;i++){
			for(int j=0;j<image_height;j++){
				image.setRGB(i, j, pixel[i][j]);
			}
		}
	}
	
	private void initPixelsAtNS(int[][] pixel){
		double max=(double)pixel[0].length-1;
		for(int i=0;i<pixel[0].length;i++){
			int val=grad.getColorByInt((double)i/max);
			for(int j=0;j<pixel.length;j++){
				pixel[j][i]=val;
			}
		}
	}
	
	private void initPixelsAtWE(int[][] pixel){
		double max=(double)pixel.length-1;
		for(int i=0;i<pixel.length;i++){
			int val=grad.getColorByInt((double)i/max);
			for(int j=0;j<pixel[i].length;j++){
				pixel[i][j]=val;
			}
		}
	}
	
	private void initPixelsAtCC(int[][] pixel){
		double cx=((double)pixel.length)/2.0;
		double cy=((double)pixel[0].length)/2.0;
		double rr=Math.sqrt(cx*cx+cy*cy);
		for(int i=0;i<pixel.length;i++){
			double xx=((double)i-cx);
			for(int j=0;j<pixel[i].length;j++){
				double yy=((double)j-cy);
				double dist=Math.sqrt(xx*xx+yy*yy);
				pixel[i][j]=grad.getColorByInt(dist/rr);
			}
		}
	}
	
	private void initPixelsAtNWSE(int[][] pixel){
		double ww=Math.sqrt((double)(image_width*image_width)+
				(double)(image_height*image_height));
		for(int i=0;i<pixel.length;i++){
			for(int j=0;j<pixel[i].length;j++){
				double dist=Math.sqrt((double)(i*i)+(double)(j*j));
				pixel[i][j]=grad.getColorByInt(dist/ww);
			}
		}
	}
	
	private void initPixelsAtSWNE(int[][] pixel){
		double ww=Math.sqrt((double)(image_width*image_width)+
				(double)(image_height*image_height));
		for(int i=0;i<pixel.length;i++){
			for(int j=0;j<pixel[i].length;j++){
				double dist=Math.sqrt((double)(i*i)+(double)(j*j));
				pixel[i][j]=grad.getColorByInt(dist/ww);
			}
		}
	}
	
	public static void main(String[] args){
		JFrame f=new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.getContentPane().setLayout(new GridLayout(2,2));
		GradientPanel[] gp=new GradientPanel[4];
		gp[0]=new GradientPanel(GradientFactory.createGradient(
				GradientFactory.GRADIENT_RGB),
				GradientPanel.GradientType.NORTH_SOUTH);
		gp[1]=new GradientPanel(GradientFactory.createGradient(
				GradientFactory.GRADIENT_NATURAL_COLOR),
				GradientPanel.GradientType.NORTHWEST_SOUTHEAST);
		gp[2]=new GradientPanel(GradientFactory.createGradient(
				new Color[]{Color.WHITE,Color.BLUE},1),
				GradientPanel.GradientType.CENTER);		gp[3]=new GradientPanel(GradientFactory.createGradient(
				new Color[]{Color.WHITE,Color.BLUE},4),
				GradientPanel.GradientType.WEST_EAST);
		for(int i=0;i<gp.length;i++){
			f.getContentPane().add(gp[i]);
		}
		f.setSize(320,240);
		f.setVisible(true);
	}
}

 ■結果
 とりあえず、こんな感じ。