1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43:
44:
45:
54: public class ColorLookUpTable
55: {
56:
59: private static float[] D50 = { 0.96422f, 1.00f, 0.82521f };
60:
61:
64: int nIn;
65:
66:
69: int nOut;
70: int nInTableEntries;
71: int nOutTableEntries;
72: int gridpoints;
73: int nClut;
74: double[][] inTable;
75: short[][] outTable;
76: double[] clut;
77: float[][] inMatrix;
78: boolean useMatrix;
79: int[] multiplier;
80: int[] offsets;
81: boolean inputLab;
82: boolean outputLab;
83:
84:
89: public ColorLookUpTable(ICC_Profile profile, int tag)
90: {
91: useMatrix = false;
92:
93: switch (tag)
94: {
95: case ICC_Profile.icSigAToB0Tag:
96: case ICC_Profile.icSigAToB1Tag:
97: case ICC_Profile.icSigAToB2Tag:
98: if (profile.getColorSpaceType() == ColorSpace.TYPE_XYZ)
99: useMatrix = true;
100: inputLab = false;
101: outputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab);
102: break;
103: case ICC_Profile.icSigBToA0Tag:
104: case ICC_Profile.icSigBToA1Tag:
105: case ICC_Profile.icSigBToA2Tag:
106: if (profile.getPCSType() == ColorSpace.TYPE_XYZ)
107: useMatrix = true;
108: inputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab);
109: outputLab = false;
110: break;
111: default:
112: throw new IllegalArgumentException("Not a clut-type tag.");
113: }
114:
115: byte[] data = profile.getData(tag);
116: if (data == null)
117: throw new IllegalArgumentException("Unsuitable profile, does not contain a CLUT.");
118:
119:
120: if (data[0] != 0x6d || data[1] != 0x66 || data[2] != 0x74)
121: throw new IllegalArgumentException("Unsuitable profile, invalid CLUT data.");
122:
123: if (data[3] == 0x32)
124: readClut16(data);
125: else if (data[3] == 0x31)
126: readClut8(data);
127: else
128: throw new IllegalArgumentException("Unknown/invalid CLUT type.");
129: }
130:
131:
134: private void readClut16(byte[] data)
135: {
136: ByteBuffer buf = ByteBuffer.wrap(data);
137:
138: nIn = data[8] & (0xFF);
139: nOut = data[9] & (0xFF);
140: nInTableEntries = buf.getShort(48);
141: nOutTableEntries = buf.getShort(50);
142: gridpoints = data[10] & (0xFF);
143:
144: inMatrix = new float[3][3];
145: for (int i = 0; i < 3; i++)
146: for (int j = 0; j < 3; j++)
147: inMatrix[i][j] = ((float) (buf.getInt(12 + (i * 3 + j) * 4))) / 65536.0f;
148:
149: inTable = new double[nIn][nInTableEntries];
150: for (int channel = 0; channel < nIn; channel++)
151: for (int i = 0; i < nInTableEntries; i++)
152: inTable[channel][i] = (double) ((int) buf.getShort(52
153: + (channel * nInTableEntries
154: + i) * 2)
155: & (0xFFFF)) / 65536.0;
156:
157: nClut = nOut;
158: multiplier = new int[nIn];
159: multiplier[nIn - 1] = nOut;
160: for (int i = 0; i < nIn; i++)
161: {
162: nClut *= gridpoints;
163: if (i > 0)
164: multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints;
165: }
166:
167: int clutOffset = 52 + nIn * nInTableEntries * 2;
168: clut = new double[nClut];
169: for (int i = 0; i < nClut; i++)
170: clut[i] = (double) ((int) buf.getShort(clutOffset + i * 2) & (0xFFFF)) / 65536.0;
171:
172: outTable = new short[nOut][nOutTableEntries];
173: for (int channel = 0; channel < nOut; channel++)
174: for (int i = 0; i < nOutTableEntries; i++)
175: outTable[channel][i] = buf.getShort(clutOffset
176: + (nClut
177: + channel * nOutTableEntries + i) * 2);
178:
179:
180: offsets = new int[(1 << nIn)];
181: offsets[0] = 0;
182: for (int j = 0; j < nIn; j++)
183: {
184: int factor = 1 << j;
185: for (int i = 0; i < factor; i++)
186: offsets[factor + i] = offsets[i] + multiplier[j];
187: }
188: }
189:
190:
193: private void readClut8(byte[] data)
194: {
195: ByteBuffer buf = ByteBuffer.wrap(data);
196:
197: nIn = (data[8] & (0xFF));
198: nOut = (data[9] & (0xFF));
199: nInTableEntries = 256;
200: nOutTableEntries = 256;
201: gridpoints = (data[10] & (0xFF));
202:
203: inMatrix = new float[3][3];
204: for (int i = 0; i < 3; i++)
205: for (int j = 0; j < 3; j++)
206: inMatrix[i][j] = ((float) (buf.getInt(12 + (i * 3 + j) * 4))) / 65536.0f;
207:
208: inTable = new double[nIn][nInTableEntries];
209: for (int channel = 0; channel < nIn; channel++)
210: for (int i = 0; i < nInTableEntries; i++)
211: inTable[channel][i] = (double) ((int) buf.get(48
212: + (channel * nInTableEntries
213: + i)) & (0xFF)) / 255.0;
214:
215: nClut = nOut;
216: multiplier = new int[nIn];
217: multiplier[nIn - 1] = nOut;
218: for (int i = 0; i < nIn; i++)
219: {
220: nClut *= gridpoints;
221: if (i > 0)
222: multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints;
223: }
224:
225: int clutOffset = 48 + nIn * nInTableEntries;
226: clut = new double[nClut];
227: for (int i = 0; i < nClut; i++)
228: clut[i] = (double) ((int) buf.get(clutOffset + i) & (0xFF)) / 255.0;
229:
230: outTable = new short[nOut][nOutTableEntries];
231: for (int channel = 0; channel < nOut; channel++)
232: for (int i = 0; i < nOutTableEntries; i++)
233: outTable[channel][i] = (short) (buf.get(clutOffset + nClut
234: + channel * nOutTableEntries
235: + i) * 257);
236:
237:
238: offsets = new int[(1 << nIn)];
239: offsets[0] = 0;
240: for (int j = 0; j < nIn; j++)
241: {
242: int factor = 1 << j;
243: for (int i = 0; i < factor; i++)
244: offsets[factor + i] = offsets[i] + multiplier[j];
245: }
246: }
247:
248:
258: float[] lookup(float[] in)
259: {
260: float[] in2 = new float[in.length];
261: if (useMatrix)
262: {
263: for (int i = 0; i < 3; i++)
264: in2[i] = in[0] * inMatrix[i][0] + in[1] * inMatrix[i][1]
265: + in[2] * inMatrix[i][2];
266: }
267: else if (inputLab)
268: in2 = XYZtoLab(in);
269: else
270: System.arraycopy(in, 0, in2, 0, in.length);
271:
272:
273: for (int i = 0; i < nIn; i++)
274: {
275: int index = (int) Math.floor(in2[i] * (double) (nInTableEntries - 1));
276:
277:
278: if (index >= nInTableEntries - 1)
279: in2[i] = (float) inTable[i][nInTableEntries - 1];
280: else if (index < 0)
281: in2[i] = (float) inTable[i][0];
282: else
283: {
284:
285: double alpha = in2[i] * ((double) nInTableEntries - 1.0) - index;
286: in2[i] = (float) (inTable[i][index] * (1 - alpha)
287: + inTable[i][index + 1] * alpha);
288: }
289: }
290:
291:
292: double[] output2 = new double[nOut];
293: double[] weights = new double[(1 << nIn)];
294: double[] clutalpha = new double[nIn];
295: int offset = 0;
296: for (int i = 0; i < nIn; i++)
297: {
298: int index = (int) Math.floor(in2[i] * ((double) gridpoints - 1.0));
299: double alpha = in2[i] * ((double) gridpoints - 1.0) - (double) index;
300:
301:
302: if (index >= gridpoints - 1)
303: {
304: index = gridpoints - 1;
305: alpha = 1.0;
306: }
307: else if (index < 0)
308: index = 0;
309: clutalpha[i] = alpha;
310: offset += index * multiplier[i];
311: }
312:
313:
314: weights[0] = 1.0;
315: for (int j = 0; j < nIn; j++)
316: {
317: int factor = 1 << j;
318: for (int i = 0; i < factor; i++)
319: {
320: weights[factor + i] = weights[i] * clutalpha[j];
321: weights[i] *= (1.0 - clutalpha[j]);
322: }
323: }
324:
325: for (int i = 0; i < nOut; i++)
326: output2[i] = weights[0] * clut[offset + i];
327:
328: for (int i = 1; i < (1 << nIn); i++)
329: {
330: int offset2 = offset + offsets[i];
331: for (int f = 0; f < nOut; f++)
332: output2[f] += weights[i] * clut[offset2 + f];
333: }
334:
335:
336: float[] output = new float[nOut];
337: for (int i = 0; i < nOut; i++)
338: {
339: int index = (int) Math.floor(output2[i] * ((double) nOutTableEntries
340: - 1.0));
341:
342:
343: if (index >= nOutTableEntries - 1)
344: output[i] = outTable[i][nOutTableEntries - 1];
345: else if (index < 0)
346: output[i] = outTable[i][0];
347: else
348: {
349:
350: double a = output2[i] * ((double) nOutTableEntries - 1.0)
351: - (double) index;
352: output[i] = (float) ((double) ((int) outTable[i][index] & (0xFFFF)) * (1
353: - a)
354: + (double) ((int) outTable[i][index + 1] & (0xFFFF)) * a) / 65536f;
355: }
356: }
357:
358: if (outputLab)
359: return LabtoXYZ(output);
360: return output;
361: }
362:
363:
366: private float[] LabtoXYZ(float[] in)
367: {
368:
369:
370:
371:
372: in[0] = (float) (100.392156862745 * in[0]);
373: in[1] = (in[1] * 256.0f) - 128.0f;
374: in[2] = (in[2] * 256.0f) - 128.0f;
375:
376: float[] out = new float[3];
377:
378: out[1] = (in[0] + 16.0f) / 116.0f;
379: out[0] = in[1] / 500.0f + out[1];
380: out[2] = out[1] - in[2] / 200.0f;
381:
382: for (int i = 0; i < 3; i++)
383: {
384: double exp = out[i] * out[i] * out[i];
385: if (exp <= 0.008856)
386: out[i] = (out[i] - 16.0f / 116.0f) / 7.787f;
387: else
388: out[i] = (float) exp;
389: out[i] = D50[i] * out[i];
390: }
391: return out;
392: }
393:
394:
397: private float[] XYZtoLab(float[] in)
398: {
399: float[] temp = new float[3];
400:
401: for (int i = 0; i < 3; i++)
402: {
403: temp[i] = in[i] / D50[i];
404:
405: if (temp[i] <= 0.008856f)
406: temp[i] = (7.7870689f * temp[i]) + (16f / 116.0f);
407: else
408: temp[i] = (float) Math.exp((1.0 / 3.0) * Math.log(temp[i]));
409: }
410:
411: float[] out = new float[3];
412: out[0] = (116.0f * temp[1]) - 16f;
413: out[1] = 500.0f * (temp[0] - temp[1]);
414: out[2] = 200.0f * (temp[1] - temp[2]);
415:
416:
417: out[0] = (float) (out[0] / 100.392156862745);
418: out[1] = (out[1] + 128f) / 256f;
419: out[2] = (out[2] + 128f) / 256f;
420: for (int i = 0; i < 3; i++)
421: {
422: if (out[i] < 0f)
423: out[i] = 0f;
424: if (out[i] > 1f)
425: out[i] = 1f;
426: }
427: return out;
428: }
429: }