package yi.iu.processor.channel;

import java.awt.*;
import yi.iu.processor.channel.ChannelMaker;

/**********************************************************************************
    (Microsoft, and Foley's "Computer Graphics: Principles and Practice",
    included) give unaccurate transforms. Actually, the transforms are really
    complicate, as given below:
    Hue = (Alpha-arctan((Red-Luminosity)*(3^0.5)/(Green-Blue)))/(2*PI)
    with { Alpha=PI/2 if Green>Blue
         { Aplha=3*PI/2 if Green<Blue
         { Hue=1 if Green=Blue
    Saturation = (Red^2+Green^2+Blue^2-Red*Green-Red*Blue-Blue*Green)^0.5
    Luminosity = (Red+Green+Blue)/3
    
    http://www.yuba.is.uec.ac.jp/~futada/futada/FAQ/color.faq
**********************************************************************************/    

 
public class HueChnlMaker extends ChannelMaker
{
    static double Alpha=Math.PI/2;
    static double Alpha3=3*Math.PI/2;
    static double sqrt3=Math.sqrt(3);
    static double sqrt3_2=sqrt3/2;
    static double PI2=Math.PI*2;
    
    static int UNDEFINED=0;
    
    public int[] getProcessed(int rgb[], Dimension d)
    {
        /*
        //test
        System.err.println("(255,0,0)->"+rgb2hue(255,0,0));
        System.err.println("(0,255,0)->"+rgb2hue(0,255,0));
        System.err.println("(0,0,255)->"+rgb2hue(0,0,255));
        System.err.println("(128,128,0)->"+rgb2hue(128, 128,0));
        System.err.println("(0,128,128)->"+rgb2hue(0,128, 128));
        System.err.println("(60,40,20)->"+rgb2hue(60,40,20));
        System.err.println("(60,20,40)->"+rgb2hue(60,20,40));
        if (true) return null;
        */
        
	    int width=d.width, height=d.height;
	    int size=width*height;
	    int[] i1= new int[size];
	    
	    for (int i=0;i<size;i++) {
	        int red=rgb[i]>>16&255;
	        int green=rgb[i]>>8&255;
	        int blue=rgb[i]&255;
	        
	        i1[i]= (int) rgb2hue(red,green,blue);
	    }
	    return i1;
    }
    
    int rgb2hue(int red, int green, int blue) {
        /*
        int luminosity = (red+green+blue)/3;
        //int saturation = (red^2+green^2+blue^2-red*green-red*blue-blue*green)^0.5
        
        double hue;
        if (green>blue)
            hue = (Alpha-Math.atan((red-luminosity)*sqrt3/(green-blue)))/(2*Math.PI);
        else if (green<blue)
            hue = (Alpha3-Math.atan((red-luminosity)*sqrt3/(green-blue)))/(2*Math.PI);
        else
            hue=1;
        return (int) (hue*255);
        */
        
        /*
        //another method: Patrick Lambert, Thierry Carron, Symbolic fusion of luminance-hue-chroma features for region segmentation,
        // Pattern Recognition 32 (1999) 1857-1872
        // Thierry Carron, Patrick Lambert, Color edge detector using jointly hue, saturation and intensity, ICIP 94,977-81, v3 
        double y= (red+green+blue)/3;           //(1/3,1/3,1/3)
        double ch1= red-(green+blue)/2;         //(1,-1/2,-1/2)
        double ch2= (blue-green)*sqrt3_2;       //(0,-3^1/2/2,3^1/2/2)
        if (ch1==0&&ch2==0) return UNDEFINED;   //red=green=blue
        double c=Math.sqrt(ch1*ch1+ch2*ch2);
        if (ch2<=0) return (int) (Math.acos(ch1/c)/PI2*256);
        else return (int) ((PI2-Math.acos(ch1/c))/PI2*256);
        //else if (ch2<0) return (int)Alpha/PI2*256;
        //else return (int)Alpha3/PI2*256;
        */
        
        //classic method:
        //Foley's "Computer Graphics: Principles and Practice"
        int max= red, min=red;
        if (green>max) max=green; if (green<min) min=green;
        if (blue>max) max=blue; if (blue<min) min=blue;
        
        int v=max;
        double s=(max!=0)?((double)(max-min)/max):0;
        double h;
        if (s==0) h=UNDEFINED;
        else {
            double delta= max-min;
            if (red==max) h=(green-blue)/delta;
            else if (green==max) h=2+(blue-red)/delta;
            else h=4+(red-green)/delta;
            h*=(256.0/6);
            if (h<0) h+=256;
        }
        return (int)h;
        
    }

}