// Offers fast sRGB encoding and decoding. // Decoding is a straightforward table look-up. // Encoding is a little more sophisticated. It's an exact conversion using about // 4 kB of look-up tables that's also still quick. It relies on the fact that // thresholds that would round to the next value have a minimum spacing of about // 0.0003. That means that any range of 0.0003 contains at most one threshold. // The to_int table contains the smaller value in the range. The threshold table // contains the threshold above which the value should be one greater. // The minimum scale value that maintains proper spacing is 255 * encode_slope // or about 3295.4. You can get away with a little bit less like 3200, taking // advantage of the alignment of thresholds, but it's not really worth it. package example.jonathan2520; public class SRGBTable { private float scale; private float[] to_float; private float[] threshold; private byte[] to_int; public SRGBTable() { this(new SRGBCalculator(), 3295.5F); } public SRGBTable(SRGBCalculator calc, float scale) { this.scale = scale; to_float = new float[256]; threshold = new float[256]; to_int = new byte[(int)scale + 1]; for (int i = 0; i < 255; ++i) { to_float[i] = (float)calc.decode(i / 255.0); double dthresh = calc.decode((i + 0.5) / 255.0); float fthresh = (float)dthresh; if (fthresh >= dthresh) fthresh = Math.nextAfter(fthresh, -1); threshold[i] = fthresh; } to_float[255] = 1; threshold[255] = Float.POSITIVE_INFINITY; int offset = 0; for (int i = 0; i < 255; ++i) { int up_to = (int)(threshold[i] * scale); build_to_int_table(offset, up_to, (byte)i); offset = up_to + 1; } build_to_int_table(offset, (int)scale, (byte)255); } private void build_to_int_table(int offset, int up_to, byte value) { if (offset > up_to) throw new IllegalArgumentException("scale is too small"); while (offset <= up_to) to_int[offset++] = value; } // x in [0, 255] public float decode(int x) { return to_float[x]; } // x in about [-0.0003, 1.00015]: tolerates rounding error on top of [0, 1] public int encode(float x) { int index = to_int[(int)(x * scale)] & 0xff; if (x > threshold[index]) ++index; return index; } }