ColorLookUpTable.java [plain text]
package gnu.java.awt.color;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
import java.nio.ByteBuffer;
public class ColorLookUpTable
{
private static float[] D50 = { 0.96422f, 1.00f, 0.82521f };
int nIn;
int nOut;
int nInTableEntries; int nOutTableEntries; int gridpoints; int nClut; double[][] inTable; short[][] outTable; double[] clut; float[][] inMatrix; boolean useMatrix; int[] multiplier;
int[] offsets; boolean inputLab; boolean outputLab;
public ColorLookUpTable(ICC_Profile profile, int tag)
{
useMatrix = false;
switch (tag)
{
case ICC_Profile.icSigAToB0Tag:
case ICC_Profile.icSigAToB1Tag:
case ICC_Profile.icSigAToB2Tag:
if (profile.getColorSpaceType() == ColorSpace.TYPE_XYZ)
useMatrix = true;
inputLab = false;
outputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab);
break;
case ICC_Profile.icSigBToA0Tag:
case ICC_Profile.icSigBToA1Tag:
case ICC_Profile.icSigBToA2Tag:
if (profile.getPCSType() == ColorSpace.TYPE_XYZ)
useMatrix = true;
inputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab);
outputLab = false;
break;
default:
throw new IllegalArgumentException("Not a clut-type tag.");
}
byte[] data = profile.getData(tag);
if (data == null)
throw new IllegalArgumentException("Unsuitable profile, does not contain a CLUT.");
if (data[0] != 0x6d || data[1] != 0x66 || data[2] != 0x74)
throw new IllegalArgumentException("Unsuitable profile, invalid CLUT data.");
if (data[3] == 0x32)
readClut16(data);
else if (data[3] == 0x31)
readClut8(data);
else
throw new IllegalArgumentException("Unknown/invalid CLUT type.");
}
private void readClut16(byte[] data)
{
ByteBuffer buf = ByteBuffer.wrap(data);
nIn = data[8] & (0xFF);
nOut = data[9] & (0xFF);
nInTableEntries = buf.getShort(48);
nOutTableEntries = buf.getShort(50);
gridpoints = data[10] & (0xFF);
inMatrix = new float[3][3];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
inMatrix[i][j] = ((float) (buf.getInt(12 + (i * 3 + j) * 4))) / 65536.0f;
inTable = new double[nIn][nInTableEntries];
for (int channel = 0; channel < nIn; channel++)
for (int i = 0; i < nInTableEntries; i++)
inTable[channel][i] = (double) ((int) buf.getShort(52
+ (channel * nInTableEntries
+ i) * 2)
& (0xFFFF)) / 65536.0;
nClut = nOut;
multiplier = new int[nIn];
multiplier[nIn - 1] = nOut;
for (int i = 0; i < nIn; i++)
{
nClut *= gridpoints;
if (i > 0)
multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints;
}
int clutOffset = 52 + nIn * nInTableEntries * 2;
clut = new double[nClut];
for (int i = 0; i < nClut; i++)
clut[i] = (double) ((int) buf.getShort(clutOffset + i * 2) & (0xFFFF)) / 65536.0;
outTable = new short[nOut][nOutTableEntries];
for (int channel = 0; channel < nOut; channel++)
for (int i = 0; i < nOutTableEntries; i++)
outTable[channel][i] = buf.getShort(clutOffset
+ (nClut
+ channel * nOutTableEntries + i) * 2);
offsets = new int[(1 << nIn)];
offsets[0] = 0;
for (int j = 0; j < nIn; j++)
{
int factor = 1 << j;
for (int i = 0; i < factor; i++)
offsets[factor + i] = offsets[i] + multiplier[j];
}
}
private void readClut8(byte[] data)
{
ByteBuffer buf = ByteBuffer.wrap(data);
nIn = (data[8] & (0xFF));
nOut = (data[9] & (0xFF));
nInTableEntries = 256; nOutTableEntries = 256; gridpoints = (data[10] & (0xFF));
inMatrix = new float[3][3];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
inMatrix[i][j] = ((float) (buf.getInt(12 + (i * 3 + j) * 4))) / 65536.0f;
inTable = new double[nIn][nInTableEntries];
for (int channel = 0; channel < nIn; channel++)
for (int i = 0; i < nInTableEntries; i++)
inTable[channel][i] = (double) ((int) buf.get(48
+ (channel * nInTableEntries
+ i)) & (0xFF)) / 255.0;
nClut = nOut;
multiplier = new int[nIn];
multiplier[nIn - 1] = nOut;
for (int i = 0; i < nIn; i++)
{
nClut *= gridpoints;
if (i > 0)
multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints;
}
int clutOffset = 48 + nIn * nInTableEntries;
clut = new double[nClut];
for (int i = 0; i < nClut; i++)
clut[i] = (double) ((int) buf.get(clutOffset + i) & (0xFF)) / 255.0;
outTable = new short[nOut][nOutTableEntries];
for (int channel = 0; channel < nOut; channel++)
for (int i = 0; i < nOutTableEntries; i++)
outTable[channel][i] = (short) (buf.get(clutOffset + nClut
+ channel * nOutTableEntries
+ i) * 257);
offsets = new int[(1 << nIn)];
offsets[0] = 0;
for (int j = 0; j < nIn; j++)
{
int factor = 1 << j;
for (int i = 0; i < factor; i++)
offsets[factor + i] = offsets[i] + multiplier[j];
}
}
float[] lookup(float[] in)
{
float[] in2 = new float[in.length];
if (useMatrix)
{
for (int i = 0; i < 3; i++)
in2[i] = in[0] * inMatrix[i][0] + in[1] * inMatrix[i][1]
+ in[2] * inMatrix[i][2];
}
else if (inputLab)
in2 = XYZtoLab(in);
else
System.arraycopy(in, 0, in2, 0, in.length);
for (int i = 0; i < nIn; i++)
{
int index = (int) Math.floor(in2[i] * (double) (nInTableEntries - 1));
if (index >= nInTableEntries - 1)
in2[i] = (float) inTable[i][nInTableEntries - 1];
else if (index < 0)
in2[i] = (float) inTable[i][0];
else
{
double alpha = in2[i] * ((double) nInTableEntries - 1.0) - index;
in2[i] = (float) (inTable[i][index] * (1 - alpha)
+ inTable[i][index + 1] * alpha);
}
}
double[] output2 = new double[nOut];
double[] weights = new double[(1 << nIn)];
double[] clutalpha = new double[nIn]; int offset = 0; for (int i = 0; i < nIn; i++)
{
int index = (int) Math.floor(in2[i] * ((double) gridpoints - 1.0));
double alpha = in2[i] * ((double) gridpoints - 1.0) - (double) index;
if (index >= gridpoints - 1)
{
index = gridpoints - 1;
alpha = 1.0;
}
else if (index < 0)
index = 0;
clutalpha[i] = alpha;
offset += index * multiplier[i];
}
weights[0] = 1.0;
for (int j = 0; j < nIn; j++)
{
int factor = 1 << j;
for (int i = 0; i < factor; i++)
{
weights[factor + i] = weights[i] * clutalpha[j];
weights[i] *= (1.0 - clutalpha[j]);
}
}
for (int i = 0; i < nOut; i++)
output2[i] = weights[0] * clut[offset + i];
for (int i = 1; i < (1 << nIn); i++)
{
int offset2 = offset + offsets[i];
for (int f = 0; f < nOut; f++)
output2[f] += weights[i] * clut[offset2 + f];
}
float[] output = new float[nOut];
for (int i = 0; i < nOut; i++)
{
int index = (int) Math.floor(output2[i] * ((double) nOutTableEntries
- 1.0));
if (index >= nOutTableEntries - 1)
output[i] = outTable[i][nOutTableEntries - 1];
else if (index < 0)
output[i] = outTable[i][0];
else
{
double a = output2[i] * ((double) nOutTableEntries - 1.0)
- (double) index;
output[i] = (float) ((double) ((int) outTable[i][index] & (0xFFFF)) * (1
- a)
+ (double) ((int) outTable[i][index + 1] & (0xFFFF)) * a) / 65536f;
}
}
if (outputLab)
return LabtoXYZ(output);
return output;
}
private float[] LabtoXYZ(float[] in)
{
in[0] = (float) (100.392156862745 * in[0]);
in[1] = (in[1] * 256.0f) - 128.0f;
in[2] = (in[2] * 256.0f) - 128.0f;
float[] out = new float[3];
out[1] = (in[0] + 16.0f) / 116.0f;
out[0] = in[1] / 500.0f + out[1];
out[2] = out[1] - in[2] / 200.0f;
for (int i = 0; i < 3; i++)
{
double exp = out[i] * out[i] * out[i];
if (exp <= 0.008856)
out[i] = (out[i] - 16.0f / 116.0f) / 7.787f;
else
out[i] = (float) exp;
out[i] = D50[i] * out[i];
}
return out;
}
private float[] XYZtoLab(float[] in)
{
float[] temp = new float[3];
for (int i = 0; i < 3; i++)
{
temp[i] = in[i] / D50[i];
if (temp[i] <= 0.008856f)
temp[i] = (7.7870689f * temp[i]) + (16f / 116.0f);
else
temp[i] = (float) Math.exp((1.0 / 3.0) * Math.log(temp[i]));
}
float[] out = new float[3];
out[0] = (116.0f * temp[1]) - 16f;
out[1] = 500.0f * (temp[0] - temp[1]);
out[2] = 200.0f * (temp[1] - temp[2]);
out[0] = (float) (out[0] / 100.392156862745);
out[1] = (out[1] + 128f) / 256f;
out[2] = (out[2] + 128f) / 256f;
for (int i = 0; i < 3; i++)
{
if (out[i] < 0f)
out[i] = 0f;
if (out[i] > 1f)
out[i] = 1f;
}
return out;
}
}