// Averaging of texels for mipmap generation.

package example.jonathan2520;

public class SRGBAverager {
	private static SRGBTable srgb = new SRGBTable();
	
	public static int average(int c0, int c1, int c2, int c3) {
		if ((((c0 | c1 | c2 | c3) ^ (c0 & c1 & c2 & c3)) & 0xff000000) == 0) {
			// Alpha values are all equal. Simplifies computation somewhat. It's
			// also a reasonable fallback when all alpha values are zero, in
			// which case the resulting color would normally be undefined.
			// Defining it like this allows code that uses invisible colors for
			// whatever reason to work. Note that Minecraft's original code
			// would set the color to black; this is added functionality.
			
			float r = srgb.decode(c0 & 0xff)
			        + srgb.decode(c1 & 0xff)
			        + srgb.decode(c2 & 0xff)
			        + srgb.decode(c3 & 0xff);
			
			float g = srgb.decode(c0 >> 8 & 0xff)
			        + srgb.decode(c1 >> 8 & 0xff)
			        + srgb.decode(c2 >> 8 & 0xff)
			        + srgb.decode(c3 >> 8 & 0xff);
			
			float b = srgb.decode(c0 >> 16 & 0xff)
			        + srgb.decode(c1 >> 16 & 0xff)
			        + srgb.decode(c2 >> 16 & 0xff)
			        + srgb.decode(c3 >> 16 & 0xff);
			
			return srgb.encode(0.25F * r)
			     | srgb.encode(0.25F * g) << 8
			     | srgb.encode(0.25F * b) << 16
			     | c0 & 0xff000000;
		} else {
			// The general case. Well-defined if at least one alpha value is
			// not zero. If you do try to process all zeros, you get
			// r = g = b = a = 0 which will NaN out in the division and produce
			// invisible black. You could remove the other case if you're okay
			// with that, but mind that producing or consuming a NaN causes an
			// extremely slow exception handler to be run on many CPUs.
			
			float a0 = c0 >>> 24;
			float a1 = c1 >>> 24;
			float a2 = c2 >>> 24;
			float a3 = c3 >>> 24;
			
			float r = a0 * srgb.decode(c0 & 0xff)
			        + a1 * srgb.decode(c1 & 0xff)
			        + a2 * srgb.decode(c2 & 0xff)
			        + a3 * srgb.decode(c3 & 0xff);
			
			float g = a0 * srgb.decode(c0 >> 8 & 0xff)
			        + a1 * srgb.decode(c1 >> 8 & 0xff)
			        + a2 * srgb.decode(c2 >> 8 & 0xff)
			        + a3 * srgb.decode(c3 >> 8 & 0xff);
			
			float b = a0 * srgb.decode(c0 >> 16 & 0xff)
			        + a1 * srgb.decode(c1 >> 16 & 0xff)
			        + a2 * srgb.decode(c2 >> 16 & 0xff)
			        + a3 * srgb.decode(c3 >> 16 & 0xff);
			
			float a = a0 + a1 + a2 + a3;
			
			return srgb.encode(r / a)
			     | srgb.encode(g / a) << 8
			     | srgb.encode(b / a) << 16
			     | (int)(0.25F * a + 0.5F) << 24;
		}
	}
}