/**
***   pgTest.java, Rich Franzen, 22 July 1999
***	mailto:rfranzen@my-deja.com
***
***	(c) 1999, Rich Franzen
***	May be used without restriction, but I would appreciate
***	credit if you implement this technique in your own code.
***
***   This program tests the two getPseudoGrey() methods, which
*** provide a means to accurately encode and decode 1786 levels
*** of grey in a 24-bit color.  A third method, isPseudoGrey(),
*** provides a test to determine whether a Color should be
*** interpreted as grey.
***
*** rev 1.1, 26 July 99:  condensed 1st method for readability
**/

import java.awt.*;

class pgTest
{
    /**
    ***   Function getPseudoGrey() returns a deep-greyscale
    *** value encoded in 24-bit colorspace.  It can uniquely
    *** generate 1,786 levels of grey without degrading from the
    *** color image source.  1786 == (255 * 7) + 1;
    **/
    public static Color getPseudoGrey(float grey)
    {
	int	i4k, boost, r, g, b;
	Color	pseudoGrey;

	i4k = (int)(grey * 4095);
	if (i4k >= 4095)
	{   // only one case of "infinity",
	    // but the >= also protects against over-bounding (r,g,b)
	    r = 255;
	    g = 255;
	    b = 255;
	}
	  else if (i4k >= 0)
	  { // normal case
	    if (i4k < 0xfe0)
	    {
		r = g = b = i4k >> 4;
		boost = i4k & 0x00f;	// 16 possibilities
	    }
	      else
	      { // accelerate rate near "infinity"
		r = g = b = 0xfe;	// base level of 254
		boost  = i4k & 0x01f;	// 32 possibilities
		boost /= 2;		// back down to 16
	      }

	    if (boost >= 14)
	    {
		r += 1;
		g += 1;
	    }
	      else if (boost >= 11)
	      {
		g += 1;
		b += 1;
	      }
	      else if (boost >= 9)
		g += 1;
	      else if (boost >= 7)
	      {
		r += 1;
		b += 1;
	      }
	      else if (boost >= 5)
		r += 1;
	      else if (boost >= 2)
		b += 1;
	  }
	  else
	  { // protect against error condition of negative grey
	    r = 0;
	    g = 0;
	    b = 0;
	  }

	pseudoGrey = new Color(r, g, b);
	return(pseudoGrey);
    }

    /**
    ***   This version of getPseudoGrey() does the opposite.  It takes
    *** a color and returns a float valued between 0.0 and 1.0.  If the
    *** elements are within 1 of each other, the pseudoGrey mapping is
    *** used, otherwise a luma calculation is made from the components.
    *** These methodologies are basically similar, with the pseudoGrey
    *** mapping being a rounded form of the luma calculation, and always
    *** scaled to 4095.
    **/
    public static float getPseudoGrey(Color c)
    {
	int	red, green, blue;
	int	cMin, cMax;
	int	base12;
	float	grey;

	red   = c.getRed();
	green = c.getGreen();
	blue  = c.getBlue();
	cMin  = Math.min(red, Math.min(green, blue));
	cMax  = Math.max(red, Math.max(green, blue));

	if (cMin == 255)
	    grey = 1.0f;
	  else if (cMax == cMin)
	  { // short-cut this common case
	    base12 = cMin << 4;
	    grey = base12 / 4095.0f;
	  }
	  else if ((cMax - cMin) < 2)
	  { // pseudoGrey
	    int	delta = 0;			// luma weights:
	    if (cMax == blue)   delta += 2;	// .114 * 16 == 1.824
	    if (cMax == red)    delta += 5;	// .299 * 16 == 4.784
	    if (cMax == green)  delta += 9;	// .587 * 16 == 9.392
	    if (cMin == 254)    delta *= 2;  // accelerate near "infinity"
	    base12 = (cMin << 4) + delta;
	    grey = base12 / 4095.0f;
	  }
	  else
	  { // use luma conversion
	    grey  = (.299f*red + .587f*green + .114f*blue) / 255.0f;
	  }

	return(grey);
    }

    /**
    ***   Boolean method isPseudoGrey() tests to determine if
    *** the supplied color is encoded as pseudoGrey.
    **/
    public static boolean isPseudoGrey(Color c)
    {
	int	cMin, cMax;
	int	red, green, blue;

	red   = c.getRed();
	green = c.getGreen();
	blue  = c.getBlue();
	cMin = Math.min(red, Math.min(green, blue));
	cMax = Math.max(red, Math.max(green, blue));

	return((cMax - cMin) < 2);
    }

    public static void main(String[] args)
    {
	float	grey;
	Color	c = null;

	if (args.length < 1)
	{
	    System.out.println(
		"I need a float, a 12-bit int, or an RGB color triplet.");
	    System.out.println("  java pgTest .798");
	    System.out.println("  java pgTest 4083");
	    System.out.println("  java pgTest 200 201 200");
	}
	  else if (args.length < 3)
	  { // grey to color
	    Float	greyS = null;

	    greyS = Float.valueOf(args[0]);
	    grey = greyS.floatValue();
	    if (args[0].equals("1") || (grey > 1.0f))
		grey /= 4095f;	// assume 12-bit input
	    c = getPseudoGrey(grey);
	    System.out.println(grey + " = " + c);
	  }
	  else
	  { // color to grey
	    Integer	redS, greenS, blueS;
	    int		r, g, b;

	    redS   = Integer.valueOf(args[0]);
	    greenS = Integer.valueOf(args[1]);
	    blueS  = Integer.valueOf(args[2]);
	    r = redS.intValue();
	    if (r < 0)
		r = 0;
	      else if (r > 255)
	        r = 255;
	    g = greenS.intValue();
	    if (g < 0)
		g = 0;
	      else if (g > 255)
	        g = 255;
	    b = blueS.intValue();
	    if (b < 0)
		b = 0;
	      else if (b > 255)
	        b = 255;
	    c = new Color(r, g, b);
	    grey = getPseudoGrey(c);

	    if (isPseudoGrey(c))
		System.out.print("pseudoGrey ");
	      else
		System.out.print("lumaGrey ");
	    System.out.println(c + " = " + grey + " = " + (int)(4095 * grey));

	  }
    }
}

