1 /* ColorLookUpTable.java -- ICC v2 CLUT
2 Copyright (C) 2004 Free Software Foundation
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version.
39 package gnu.java.awt.color;
41 import java.awt.color.ColorSpace;
42 import java.awt.color.ICC_Profile;
43 import java.nio.ByteBuffer;
47 * ColorLookUpTable handles color lookups through a color lookup table,
48 * as defined in the ICC specification.
49 * Both 'mft2' and 'mft1' (8 and 16-bit) type CLUTs are handled.
51 * This will have to be updated later for ICC 4.0.0
53 * @author Sven de Marothy
55 public class ColorLookUpTable
58 * CIE 1931 D50 white point (in Lab coordinates)
60 private static float[] D50 = { 0.96422f, 1.00f, 0.82521f };
63 * Number of input/output channels
68 * Number of input/output channels
71 int nInTableEntries; // Number of input table entries
72 int nOutTableEntries; // Number of output table entries
73 int gridpoints; // Number of gridpoints
74 int nClut; // This is nOut*(gridpoints**nIn)
75 double[][] inTable; // 1D input table ([channel][table])
76 short[][] outTable; // 1D input table ([channel][table])
77 double[] clut; // The color lookup table
78 float[][] inMatrix; // input matrix (XYZ only)
79 boolean useMatrix; // Whether to use the matrix or not.
81 int[] offsets; // Hypercube offsets
82 boolean inputLab; // Set if the CLUT input CS is Lab
83 boolean outputLab; // Set if the CLUT output CS is Lab
87 * Requires a profile file to get the CLUT from and the tag of the
88 * CLUT to create. (icSigXToYZTag where X,Y = [A | B], Z = [0,1,2])
90 public ColorLookUpTable(ICC_Profile profile, int tag)
96 case ICC_Profile.icSigAToB0Tag:
97 case ICC_Profile.icSigAToB1Tag:
98 case ICC_Profile.icSigAToB2Tag:
99 if (profile.getColorSpaceType() == ColorSpace.TYPE_XYZ)
102 outputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab);
104 case ICC_Profile.icSigBToA0Tag:
105 case ICC_Profile.icSigBToA1Tag:
106 case ICC_Profile.icSigBToA2Tag:
107 if (profile.getPCSType() == ColorSpace.TYPE_XYZ)
109 inputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab);
113 throw new IllegalArgumentException("Not a clut-type tag.");
116 byte[] data = profile.getData(tag);
118 throw new IllegalArgumentException("Unsuitable profile, does not contain a CLUT.");
121 if (data[0] != 0x6d || data[1] != 0x66 || data[2] != 0x74)
122 throw new IllegalArgumentException("Unsuitable profile, invalid CLUT data.");
126 else if (data[3] == 0x31)
129 throw new IllegalArgumentException("Unknown/invalid CLUT type.");
133 * Loads a 16-bit CLUT into our data structures
135 private void readClut16(byte[] data)
137 ByteBuffer buf = ByteBuffer.wrap(data);
139 nIn = data[8] & (0xFF);
140 nOut = data[9] & (0xFF);
141 nInTableEntries = buf.getShort(48);
142 nOutTableEntries = buf.getShort(50);
143 gridpoints = data[10] & (0xFF);
145 inMatrix = new float[3][3];
146 for (int i = 0; i < 3; i++)
147 for (int j = 0; j < 3; j++)
148 inMatrix[i][j] = ((float) (buf.getInt(12 + (i * 3 + j) * 4))) / 65536.0f;
150 inTable = new double[nIn][nInTableEntries];
151 for (int channel = 0; channel < nIn; channel++)
152 for (int i = 0; i < nInTableEntries; i++)
153 inTable[channel][i] = (double) ((int) buf.getShort(52
154 + (channel * nInTableEntries
156 & (0xFFFF)) / 65536.0;
159 multiplier = new int[nIn];
160 multiplier[nIn - 1] = nOut;
161 for (int i = 0; i < nIn; i++)
165 multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints;
168 int clutOffset = 52 + nIn * nInTableEntries * 2;
169 clut = new double[nClut];
170 for (int i = 0; i < nClut; i++)
171 clut[i] = (double) ((int) buf.getShort(clutOffset + i * 2) & (0xFFFF)) / 65536.0;
173 outTable = new short[nOut][nOutTableEntries];
174 for (int channel = 0; channel < nOut; channel++)
175 for (int i = 0; i < nOutTableEntries; i++)
176 outTable[channel][i] = buf.getShort(clutOffset
178 + channel * nOutTableEntries + i) * 2);
180 // calculate the hypercube corner offsets
181 offsets = new int[(1 << nIn)];
183 for (int j = 0; j < nIn; j++)
186 for (int i = 0; i < factor; i++)
187 offsets[factor + i] = offsets[i] + multiplier[j];
192 * Loads a 8-bit CLUT into our data structures.
194 private void readClut8(byte[] data)
196 ByteBuffer buf = ByteBuffer.wrap(data);
198 nIn = (data[8] & (0xFF));
199 nOut = (data[9] & (0xFF));
200 nInTableEntries = 256; // always 256
201 nOutTableEntries = 256; // always 256
202 gridpoints = (data[10] & (0xFF));
204 inMatrix = new float[3][3];
205 for (int i = 0; i < 3; i++)
206 for (int j = 0; j < 3; j++)
207 inMatrix[i][j] = ((float) (buf.getInt(12 + (i * 3 + j) * 4))) / 65536.0f;
209 inTable = new double[nIn][nInTableEntries];
210 for (int channel = 0; channel < nIn; channel++)
211 for (int i = 0; i < nInTableEntries; i++)
212 inTable[channel][i] = (double) ((int) buf.get(48
213 + (channel * nInTableEntries
214 + i)) & (0xFF)) / 255.0;
217 multiplier = new int[nIn];
218 multiplier[nIn - 1] = nOut;
219 for (int i = 0; i < nIn; i++)
223 multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints;
226 int clutOffset = 48 + nIn * nInTableEntries;
227 clut = new double[nClut];
228 for (int i = 0; i < nClut; i++)
229 clut[i] = (double) ((int) buf.get(clutOffset + i) & (0xFF)) / 255.0;
231 outTable = new short[nOut][nOutTableEntries];
232 for (int channel = 0; channel < nOut; channel++)
233 for (int i = 0; i < nOutTableEntries; i++)
234 outTable[channel][i] = (short) (buf.get(clutOffset + nClut
235 + channel * nOutTableEntries
238 // calculate the hypercube corner offsets
239 offsets = new int[(1 << nIn)];
241 for (int j = 0; j < nIn; j++)
244 for (int i = 0; i < factor; i++)
245 offsets[factor + i] = offsets[i] + multiplier[j];
250 * Performs a lookup through the Color LookUp Table.
251 * If the CLUT tag type is AtoB the conversion will be from the device
252 * color space to the PCS, BtoA type goes in the opposite direction.
254 * For convenience, the PCS values for input or output will always be
255 * CIE XYZ (D50), if the actual PCS is Lab, the values will be converted.
257 * N-dimensional linear interpolation is used.
259 float[] lookup(float[] in)
261 float[] in2 = new float[in.length];
264 for (int i = 0; i < 3; i++)
265 in2[i] = in[0] * inMatrix[i][0] + in[1] * inMatrix[i][1]
266 + in[2] * inMatrix[i][2];
271 System.arraycopy(in, 0, in2, 0, in.length);
274 for (int i = 0; i < nIn; i++)
276 int index = (int) Math.floor(in2[i] * (double) (nInTableEntries - 1)); // floor in
279 if (index >= nInTableEntries - 1)
280 in2[i] = (float) inTable[i][nInTableEntries - 1];
282 in2[i] = (float) inTable[i][0];
285 // linear interpolation
286 double alpha = in2[i] * ((double) nInTableEntries - 1.0) - index;
287 in2[i] = (float) (inTable[i][index] * (1 - alpha)
288 + inTable[i][index + 1] * alpha);
293 double[] output2 = new double[nOut];
294 double[] weights = new double[(1 << nIn)];
295 double[] clutalpha = new double[nIn]; // interpolation values
296 int offset = 0; // = gp
297 for (int i = 0; i < nIn; i++)
299 int index = (int) Math.floor(in2[i] * ((double) gridpoints - 1.0));
300 double alpha = in2[i] * ((double) gridpoints - 1.0) - (double) index;
303 if (index >= gridpoints - 1)
305 index = gridpoints - 1;
310 clutalpha[i] = alpha;
311 offset += index * multiplier[i];
314 // Calculate interpolation weights
316 for (int j = 0; j < nIn; j++)
319 for (int i = 0; i < factor; i++)
321 weights[factor + i] = weights[i] * clutalpha[j];
322 weights[i] *= (1.0 - clutalpha[j]);
326 for (int i = 0; i < nOut; i++)
327 output2[i] = weights[0] * clut[offset + i];
329 for (int i = 1; i < (1 << nIn); i++)
331 int offset2 = offset + offsets[i];
332 for (int f = 0; f < nOut; f++)
333 output2[f] += weights[i] * clut[offset2 + f];
337 float[] output = new float[nOut];
338 for (int i = 0; i < nOut; i++)
340 int index = (int) Math.floor(output2[i] * ((double) nOutTableEntries
344 if (index >= nOutTableEntries - 1)
345 output[i] = outTable[i][nOutTableEntries - 1];
347 output[i] = outTable[i][0];
350 // linear interpolation
351 double a = output2[i] * ((double) nOutTableEntries - 1.0)
353 output[i] = (float) ((double) ((int) outTable[i][index] & (0xFFFF)) * (1
355 + (double) ((int) outTable[i][index + 1] & (0xFFFF)) * a) / 65536f;
360 return LabtoXYZ(output);
365 * Converts CIE Lab coordinates to (D50) XYZ ones.
367 private float[] LabtoXYZ(float[] in)
369 // Convert from byte-packed format to a
370 // more convenient one (actual Lab values)
371 // (See ICC spec for details)
372 // factor is 100 * 65536 / 65280
373 in[0] = (float) (100.392156862745 * in[0]);
374 in[1] = (in[1] * 256.0f) - 128.0f;
375 in[2] = (in[2] * 256.0f) - 128.0f;
377 float[] out = new float[3];
379 out[1] = (in[0] + 16.0f) / 116.0f;
380 out[0] = in[1] / 500.0f + out[1];
381 out[2] = out[1] - in[2] / 200.0f;
383 for (int i = 0; i < 3; i++)
385 double exp = out[i] * out[i] * out[i];
387 out[i] = (out[i] - 16.0f / 116.0f) / 7.787f;
389 out[i] = (float) exp;
390 out[i] = D50[i] * out[i];
396 * Converts CIE XYZ coordinates to Lab ones.
398 private float[] XYZtoLab(float[] in)
400 float[] temp = new float[3];
402 for (int i = 0; i < 3; i++)
404 temp[i] = in[i] / D50[i];
406 if (temp[i] <= 0.008856f)
407 temp[i] = (7.7870689f * temp[i]) + (16f / 116.0f);
409 temp[i] = (float) Math.exp((1.0 / 3.0) * Math.log(temp[i]));
412 float[] out = new float[3];
413 out[0] = (116.0f * temp[1]) - 16f;
414 out[1] = 500.0f * (temp[0] - temp[1]);
415 out[2] = 200.0f * (temp[1] - temp[2]);
417 // Normalize to packed format
418 out[0] = (float) (out[0] / 100.392156862745);
419 out[1] = (out[1] + 128f) / 256f;
420 out[2] = (out[2] + 128f) / 256f;
421 for (int i = 0; i < 3; i++)