OSDN Git Service

138d27adb351ba47c06fadf6cd71fc622acf3225
[pf3gnuchains/gcc-fork.git] / libjava / gnu / java / awt / color / ColorLookUpTable.java
1 /* ColorLookUpTable.java -- ICC v2 CLUT
2    Copyright (C) 2004 Free Software Foundation
3
4    This file is part of GNU Classpath.
5
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)
9    any later version.
10
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.
15
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
19    02111-1307 USA.
20
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
24    combination.
25
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.
37 */
38
39 package gnu.java.awt.color;
40
41 import java.awt.color.ColorSpace;
42 import java.awt.color.ICC_Profile;
43 import java.nio.ByteBuffer;
44
45
46 /**
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.
50  *
51  * This will have to be updated later for ICC 4.0.0
52  *
53  * @author Sven de Marothy
54  */
55 public class ColorLookUpTable
56 {
57   /**
58    * CIE 1931 D50 white point (in Lab coordinates)
59    */
60   private static float[] D50 = { 0.96422f, 1.00f, 0.82521f };
61
62   /**
63    * Number of input/output channels
64    */
65   int nIn;
66
67   /**
68    * Number of input/output channels
69    */
70   int nOut;
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.
80   int[] multiplier;
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
84
85   /**
86    * Constructor
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])
89    */
90   public ColorLookUpTable(ICC_Profile profile, int tag)
91   {
92     useMatrix = false;
93
94     switch (tag)
95       {
96       case ICC_Profile.icSigAToB0Tag:
97       case ICC_Profile.icSigAToB1Tag:
98       case ICC_Profile.icSigAToB2Tag:
99         if (profile.getColorSpaceType() == ColorSpace.TYPE_XYZ)
100           useMatrix = true;
101         inputLab = false;
102         outputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab);
103         break;
104       case ICC_Profile.icSigBToA0Tag:
105       case ICC_Profile.icSigBToA1Tag:
106       case ICC_Profile.icSigBToA2Tag:
107         if (profile.getPCSType() == ColorSpace.TYPE_XYZ)
108           useMatrix = true;
109         inputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab);
110         outputLab = false;
111         break;
112       default:
113         throw new IllegalArgumentException("Not a clut-type tag.");
114       }
115
116     byte[] data = profile.getData(tag);
117     if (data == null)
118       throw new IllegalArgumentException("Unsuitable profile, does not contain a CLUT.");
119
120     // check 'mft'
121     if (data[0] != 0x6d || data[1] != 0x66 || data[2] != 0x74)
122       throw new IllegalArgumentException("Unsuitable profile, invalid CLUT data.");
123
124     if (data[3] == 0x32)
125       readClut16(data);
126     else if (data[3] == 0x31)
127       readClut8(data);
128     else
129       throw new IllegalArgumentException("Unknown/invalid CLUT type.");
130   }
131
132   /**
133    * Loads a 16-bit CLUT into our data structures
134    */
135   private void readClut16(byte[] data)
136   {
137     ByteBuffer buf = ByteBuffer.wrap(data);
138
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);
144
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;
149
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
155                                                            + i) * 2)
156                               & (0xFFFF)) / 65536.0;
157
158     nClut = nOut;
159     multiplier = new int[nIn];
160     multiplier[nIn - 1] = nOut;
161     for (int i = 0; i < nIn; i++)
162       {
163         nClut *= gridpoints;
164         if (i > 0)
165           multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints;
166       }
167
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;
172
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
177                                             + (nClut
178                                             + channel * nOutTableEntries + i) * 2);
179
180     // calculate the hypercube corner offsets
181     offsets = new int[(1 << nIn)];
182     offsets[0] = 0;
183     for (int j = 0; j < nIn; j++)
184       {
185         int factor = 1 << j;
186         for (int i = 0; i < factor; i++)
187           offsets[factor + i] = offsets[i] + multiplier[j];
188       }
189   }
190
191   /**
192    * Loads a 8-bit CLUT into our data structures.
193    */
194   private void readClut8(byte[] data)
195   {
196     ByteBuffer buf = ByteBuffer.wrap(data);
197
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));
203
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;
208
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;
215
216     nClut = nOut;
217     multiplier = new int[nIn];
218     multiplier[nIn - 1] = nOut;
219     for (int i = 0; i < nIn; i++)
220       {
221         nClut *= gridpoints;
222         if (i > 0)
223           multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints;
224       }
225
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;
230
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
236                                                 + i) * 257);
237
238     // calculate the hypercube corner offsets
239     offsets = new int[(1 << nIn)];
240     offsets[0] = 0;
241     for (int j = 0; j < nIn; j++)
242       {
243         int factor = 1 << j;
244         for (int i = 0; i < factor; i++)
245           offsets[factor + i] = offsets[i] + multiplier[j];
246       }
247   }
248
249   /**
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.
253    *
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.
256    *
257    * N-dimensional linear interpolation is used.
258    */
259   float[] lookup(float[] in)
260   {
261     float[] in2 = new float[in.length];
262     if (useMatrix)
263       {
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];
267       }
268     else if (inputLab)
269       in2 = XYZtoLab(in);
270     else
271       System.arraycopy(in, 0, in2, 0, in.length);
272
273     // input table 
274     for (int i = 0; i < nIn; i++)
275       {
276         int index = (int) Math.floor(in2[i] * (double) (nInTableEntries - 1)); // floor in
277
278         // clip values.
279         if (index >= nInTableEntries - 1)
280           in2[i] = (float) inTable[i][nInTableEntries - 1];
281         else if (index < 0)
282           in2[i] = (float) inTable[i][0];
283         else
284           {
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);
289           }
290       }
291
292     // CLUT lookup
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++)
298       {
299         int index = (int) Math.floor(in2[i] * ((double) gridpoints - 1.0));
300         double alpha = in2[i] * ((double) gridpoints - 1.0) - (double) index;
301
302         // clip values.
303         if (index >= gridpoints - 1)
304           {
305             index = gridpoints - 1;
306             alpha = 1.0;
307           }
308         else if (index < 0)
309           index = 0;
310         clutalpha[i] = alpha;
311         offset += index * multiplier[i];
312       }
313
314     // Calculate interpolation weights
315     weights[0] = 1.0;
316     for (int j = 0; j < nIn; j++)
317       {
318         int factor = 1 << j;
319         for (int i = 0; i < factor; i++)
320           {
321             weights[factor + i] = weights[i] * clutalpha[j];
322             weights[i] *= (1.0 - clutalpha[j]);
323           }
324       }
325
326     for (int i = 0; i < nOut; i++)
327       output2[i] = weights[0] * clut[offset + i];
328
329     for (int i = 1; i < (1 << nIn); i++)
330       {
331         int offset2 = offset + offsets[i];
332         for (int f = 0; f < nOut; f++)
333           output2[f] += weights[i] * clut[offset2 + f];
334       }
335
336     // output table 
337     float[] output = new float[nOut];
338     for (int i = 0; i < nOut; i++)
339       {
340         int index = (int) Math.floor(output2[i] * ((double) nOutTableEntries
341                                      - 1.0));
342
343         // clip values.
344         if (index >= nOutTableEntries - 1)
345           output[i] = outTable[i][nOutTableEntries - 1];
346         else if (index < 0)
347           output[i] = outTable[i][0];
348         else
349           {
350             // linear interpolation
351             double a = output2[i] * ((double) nOutTableEntries - 1.0)
352                        - (double) index;
353             output[i] = (float) ((double) ((int) outTable[i][index] & (0xFFFF)) * (1
354                         - a)
355                         + (double) ((int) outTable[i][index + 1] & (0xFFFF)) * a) / 65536f;
356           }
357       }
358
359     if (outputLab)
360       return LabtoXYZ(output);
361     return output;
362   }
363
364   /**
365    * Converts CIE Lab coordinates to (D50) XYZ ones.
366    */
367   private float[] LabtoXYZ(float[] in)
368   {
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;
376
377     float[] out = new float[3];
378
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;
382
383     for (int i = 0; i < 3; i++)
384       {
385         double exp = out[i] * out[i] * out[i];
386         if (exp <= 0.008856)
387           out[i] = (out[i] - 16.0f / 116.0f) / 7.787f;
388         else
389           out[i] = (float) exp;
390         out[i] = D50[i] * out[i];
391       }
392     return out;
393   }
394
395   /**
396    * Converts CIE XYZ coordinates to Lab ones.
397    */
398   private float[] XYZtoLab(float[] in)
399   {
400     float[] temp = new float[3];
401
402     for (int i = 0; i < 3; i++)
403       {
404         temp[i] = in[i] / D50[i];
405
406         if (temp[i] <= 0.008856f)
407           temp[i] = (7.7870689f * temp[i]) + (16f / 116.0f);
408         else
409           temp[i] = (float) Math.exp((1.0 / 3.0) * Math.log(temp[i]));
410       }
411
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]);
416
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++)
422       {
423         if (out[i] < 0f)
424           out[i] = 0f;
425         if (out[i] > 1f)
426           out[i] = 1f;
427       }
428     return out;
429   }
430 }