1 /*
2  * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /**********************************************************************
27  **********************************************************************
28  **********************************************************************
29  *** COPYRIGHT (c) Eastman Kodak Company, 1997                      ***
30  *** As  an unpublished  work pursuant to Title 17 of the United    ***
31  *** States Code.  All rights reserved.                             ***
32  **********************************************************************
33  **********************************************************************
34  **********************************************************************/
35 
36 package sun.java2d.cmm.lcms;
37 
38 import java.awt.color.ICC_Profile;
39 import java.awt.color.CMMException;
40 import java.awt.color.ColorSpace;
41 import java.awt.image.BufferedImage;
42 import java.awt.image.Raster;
43 import java.awt.image.WritableRaster;
44 import java.awt.image.ColorModel;
45 import java.awt.image.SampleModel;
46 import java.awt.image.DataBuffer;
47 import sun.java2d.cmm.*;
48 import sun.java2d.cmm.lcms.*;
49 import static sun.java2d.cmm.lcms.LCMSImageLayout.ImageLayoutException;
50 
51 
52 public class LCMSTransform implements ColorTransform {
53     long ID;
54     private int inFormatter = 0;
55     private boolean isInIntPacked = false;
56     private int outFormatter = 0;
57     private boolean isOutIntPacked = false;
58 
59     ICC_Profile[] profiles;
60     LCMSProfile[] lcmsProfiles;
61     int renderType;
62     int transformType;
63 
64     private int numInComponents = -1;
65     private int numOutComponents = -1;
66 
67     private Object disposerReferent = new Object();
68 
69     /* the class initializer */
70     static {
71         if (ProfileDeferralMgr.deferring) {
ProfileDeferralMgr.activateProfiles()72             ProfileDeferralMgr.activateProfiles();
73         }
74     }
75 
LCMSTransform(ICC_Profile profile, int renderType, int transformType)76     public LCMSTransform(ICC_Profile profile, int renderType,
77                          int transformType)
78     {
79         /* Actually, it is not a complete transform but just part of it */
80         profiles = new ICC_Profile[1];
81         profiles[0] = profile;
82         lcmsProfiles = new LCMSProfile[1];
83         lcmsProfiles[0] = LCMS.getProfileID(profile);
84         this.renderType = (renderType == ColorTransform.Any)?
85                               ICC_Profile.icPerceptual : renderType;
86         this.transformType = transformType;
87 
88         /* Note that ICC_Profile.getNumComponents() is quite expensive
89          * (it may results in a reading of the profile header).
90          * So, here we cache the number of components of input and
91          * output profiles for further usage.
92          */
93         numInComponents = profiles[0].getNumComponents();
94         numOutComponents = profiles[profiles.length - 1].getNumComponents();
95     }
96 
LCMSTransform(ColorTransform[] transforms)97     public LCMSTransform (ColorTransform[] transforms) {
98         int size = 0;
99         for (int i=0; i < transforms.length; i++) {
100             size+=((LCMSTransform)transforms[i]).profiles.length;
101         }
102         profiles = new ICC_Profile[size];
103         lcmsProfiles = new LCMSProfile[size];
104         int j = 0;
105         for (int i=0; i < transforms.length; i++) {
106             LCMSTransform curTrans = (LCMSTransform)transforms[i];
107             System.arraycopy(curTrans.profiles, 0, profiles, j,
108                              curTrans.profiles.length);
109             System.arraycopy(curTrans.lcmsProfiles, 0, lcmsProfiles, j,
110                              curTrans.lcmsProfiles.length);
111             j += curTrans.profiles.length;
112         }
113         renderType = ((LCMSTransform)transforms[0]).renderType;
114 
115         /* Note that ICC_Profile.getNumComponents() is quite expensive
116          * (it may results in a reading of the profile header).
117          * So, here we cache the number of components of input and
118          * output profiles for further usage.
119          */
120         numInComponents = profiles[0].getNumComponents();
121         numOutComponents = profiles[profiles.length - 1].getNumComponents();
122     }
123 
getNumInComponents()124     public int getNumInComponents() {
125         return numInComponents;
126     }
127 
getNumOutComponents()128     public int getNumOutComponents() {
129         return numOutComponents;
130     }
131 
doTransform(LCMSImageLayout in, LCMSImageLayout out)132     private synchronized void doTransform(LCMSImageLayout in,
133                                           LCMSImageLayout out) {
134         // update native transfrom if needed
135         if (ID == 0L ||
136             inFormatter != in.pixelType || isInIntPacked != in.isIntPacked ||
137             outFormatter != out.pixelType || isOutIntPacked != out.isIntPacked)
138         {
139 
140             if (ID != 0L) {
141                 // Disposer will destroy forgotten transform
142                 disposerReferent = new Object();
143             }
144             inFormatter = in.pixelType;
145             isInIntPacked = in.isIntPacked;
146 
147             outFormatter = out.pixelType;
148             isOutIntPacked = out.isIntPacked;
149 
150             ID = LCMS.createTransform(lcmsProfiles, renderType,
151                                             inFormatter, isInIntPacked,
152                                             outFormatter, isOutIntPacked,
153                                             disposerReferent);
154         }
155 
156         LCMS.colorConvert(this, in, out);
157     }
158 
colorConvert(BufferedImage src, BufferedImage dst)159     public void colorConvert(BufferedImage src, BufferedImage dst) {
160         LCMSImageLayout srcIL, dstIL;
161         try {
162             if (!dst.getColorModel().hasAlpha()) {
163                 dstIL = LCMSImageLayout.createImageLayout(dst);
164 
165                 if (dstIL != null) {
166                     srcIL = LCMSImageLayout.createImageLayout(src);
167                     if (srcIL != null) {
168                         doTransform(srcIL, dstIL);
169                         return;
170                     }
171                 }
172             }
173         }  catch (ImageLayoutException e) {
174             throw new CMMException("Unable to convert images");
175         }
176 
177         Raster srcRas = src.getRaster();
178         WritableRaster dstRas = dst.getRaster();
179         ColorModel srcCM = src.getColorModel();
180         ColorModel dstCM = dst.getColorModel();
181         int w = src.getWidth();
182         int h = src.getHeight();
183         int srcNumComp = srcCM.getNumColorComponents();
184         int dstNumComp = dstCM.getNumColorComponents();
185         int precision = 8;
186         float maxNum = 255.0f;
187         for (int i = 0; i < srcNumComp; i++) {
188             if (srcCM.getComponentSize(i) > 8) {
189                  precision = 16;
190                  maxNum = 65535.0f;
191              }
192         }
193         for (int i = 0; i < dstNumComp; i++) {
194             if (dstCM.getComponentSize(i) > 8) {
195                  precision = 16;
196                  maxNum = 65535.0f;
197              }
198         }
199         float[] srcMinVal = new float[srcNumComp];
200         float[] srcInvDiffMinMax = new float[srcNumComp];
201         ColorSpace cs = srcCM.getColorSpace();
202         for (int i = 0; i < srcNumComp; i++) {
203             srcMinVal[i] = cs.getMinValue(i);
204             srcInvDiffMinMax[i] = maxNum / (cs.getMaxValue(i) - srcMinVal[i]);
205         }
206         cs = dstCM.getColorSpace();
207         float[] dstMinVal = new float[dstNumComp];
208         float[] dstDiffMinMax = new float[dstNumComp];
209         for (int i = 0; i < dstNumComp; i++) {
210             dstMinVal[i] = cs.getMinValue(i);
211             dstDiffMinMax[i] = (cs.getMaxValue(i) - dstMinVal[i]) / maxNum;
212         }
213         boolean dstHasAlpha = dstCM.hasAlpha();
214         boolean needSrcAlpha = srcCM.hasAlpha() && dstHasAlpha;
215         float[] dstColor;
216         if (dstHasAlpha) {
217             dstColor = new float[dstNumComp + 1];
218         } else {
219             dstColor = new float[dstNumComp];
220         }
221         if (precision == 8) {
222             byte[] srcLine = new byte[w * srcNumComp];
223             byte[] dstLine = new byte[w * dstNumComp];
224             Object pixel;
225             float[] color;
226             float[] alpha = null;
227             if (needSrcAlpha) {
228                 alpha = new float[w];
229             }
230             int idx;
231             // TODO check for src npixels = dst npixels
232             try {
233                 srcIL = new LCMSImageLayout(
234                         srcLine, srcLine.length/getNumInComponents(),
235                         LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
236                         LCMSImageLayout.BYTES_SH(1), getNumInComponents());
237                 dstIL = new LCMSImageLayout(
238                         dstLine, dstLine.length/getNumOutComponents(),
239                         LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
240                         LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
241             } catch (ImageLayoutException e) {
242                 throw new CMMException("Unable to convert images");
243             }
244             // process each scanline
245             for (int y = 0; y < h; y++) {
246                 // convert src scanline
247                 pixel = null;
248                 color = null;
249                 idx = 0;
250                 for (int x = 0; x < w; x++) {
251                     pixel = srcRas.getDataElements(x, y, pixel);
252                     color = srcCM.getNormalizedComponents(pixel, color, 0);
253                     for (int i = 0; i < srcNumComp; i++) {
254                         srcLine[idx++] = (byte)
255                             ((color[i] - srcMinVal[i]) * srcInvDiffMinMax[i] +
256                              0.5f);
257                     }
258                     if (needSrcAlpha) {
259                         alpha[x] = color[srcNumComp];
260                     }
261                 }
262                 // color convert srcLine to dstLine
263                 doTransform(srcIL, dstIL);
264 
265                 // convert dst scanline
266                 pixel = null;
267                 idx = 0;
268                 for (int x = 0; x < w; x++) {
269                     for (int i = 0; i < dstNumComp; i++) {
270                         dstColor[i] = ((float) (dstLine[idx++] & 0xff)) *
271                                       dstDiffMinMax[i] + dstMinVal[i];
272                     }
273                     if (needSrcAlpha) {
274                         dstColor[dstNumComp] = alpha[x];
275                     } else if (dstHasAlpha) {
276                         dstColor[dstNumComp] = 1.0f;
277                     }
278                     pixel = dstCM.getDataElements(dstColor, 0, pixel);
279                     dstRas.setDataElements(x, y, pixel);
280                 }
281             }
282         } else {
283             short[] srcLine = new short[w * srcNumComp];
284             short[] dstLine = new short[w * dstNumComp];
285             Object pixel;
286             float[] color;
287             float[] alpha = null;
288             if (needSrcAlpha) {
289                 alpha = new float[w];
290             }
291             int idx;
292             try {
293                 srcIL = new LCMSImageLayout(
294                     srcLine, srcLine.length/getNumInComponents(),
295                     LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
296                     LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
297 
298                 dstIL = new LCMSImageLayout(
299                     dstLine, dstLine.length/getNumOutComponents(),
300                     LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
301                     LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
302             } catch (ImageLayoutException e) {
303                 throw new CMMException("Unable to convert images");
304             }
305             // process each scanline
306             for (int y = 0; y < h; y++) {
307                 // convert src scanline
308                 pixel = null;
309                 color = null;
310                 idx = 0;
311                 for (int x = 0; x < w; x++) {
312                     pixel = srcRas.getDataElements(x, y, pixel);
313                     color = srcCM.getNormalizedComponents(pixel, color, 0);
314                     for (int i = 0; i < srcNumComp; i++) {
315                         srcLine[idx++] = (short)
316                             ((color[i] - srcMinVal[i]) * srcInvDiffMinMax[i] +
317                              0.5f);
318                     }
319                     if (needSrcAlpha) {
320                         alpha[x] = color[srcNumComp];
321                     }
322                 }
323                 // color convert srcLine to dstLine
324                 doTransform(srcIL, dstIL);
325 
326                 // convert dst scanline
327                 pixel = null;
328                 idx = 0;
329                 for (int x = 0; x < w; x++) {
330                     for (int i = 0; i < dstNumComp; i++) {
331                         dstColor[i] = ((float) (dstLine[idx++] & 0xffff)) *
332                                       dstDiffMinMax[i] + dstMinVal[i];
333                     }
334                     if (needSrcAlpha) {
335                         dstColor[dstNumComp] = alpha[x];
336                     } else if (dstHasAlpha) {
337                         dstColor[dstNumComp] = 1.0f;
338                     }
339                     pixel = dstCM.getDataElements(dstColor, 0, pixel);
340                     dstRas.setDataElements(x, y, pixel);
341                 }
342             }
343         }
344     }
345 
colorConvert(Raster src, WritableRaster dst, float[] srcMinVal, float[]srcMaxVal, float[] dstMinVal, float[]dstMaxVal)346     public void colorConvert(Raster src, WritableRaster dst,
347                              float[] srcMinVal, float[]srcMaxVal,
348                              float[] dstMinVal, float[]dstMaxVal) {
349         LCMSImageLayout srcIL, dstIL;
350 
351         // Can't pass src and dst directly to CMM, so process per scanline
352         SampleModel srcSM = src.getSampleModel();
353         SampleModel dstSM = dst.getSampleModel();
354         int srcTransferType = src.getTransferType();
355         int dstTransferType = dst.getTransferType();
356         boolean srcIsFloat, dstIsFloat;
357         if ((srcTransferType == DataBuffer.TYPE_FLOAT) ||
358             (srcTransferType == DataBuffer.TYPE_DOUBLE)) {
359             srcIsFloat = true;
360         } else {
361             srcIsFloat = false;
362         }
363         if ((dstTransferType == DataBuffer.TYPE_FLOAT) ||
364             (dstTransferType == DataBuffer.TYPE_DOUBLE)) {
365             dstIsFloat = true;
366         } else {
367             dstIsFloat = false;
368         }
369         int w = src.getWidth();
370         int h = src.getHeight();
371         int srcNumBands = src.getNumBands();
372         int dstNumBands = dst.getNumBands();
373         float[] srcScaleFactor = new float[srcNumBands];
374         float[] dstScaleFactor = new float[dstNumBands];
375         float[] srcUseMinVal = new float[srcNumBands];
376         float[] dstUseMinVal = new float[dstNumBands];
377         for (int i = 0; i < srcNumBands; i++) {
378             if (srcIsFloat) {
379                 srcScaleFactor[i] =  65535.0f / (srcMaxVal[i] - srcMinVal[i]);
380                 srcUseMinVal[i] = srcMinVal[i];
381             } else {
382                 if (srcTransferType == DataBuffer.TYPE_SHORT) {
383                     srcScaleFactor[i] = 65535.0f / 32767.0f;
384                 } else {
385                     srcScaleFactor[i] = 65535.0f /
386                         ((float) ((1 << srcSM.getSampleSize(i)) - 1));
387                 }
388                 srcUseMinVal[i] = 0.0f;
389             }
390         }
391         for (int i = 0; i < dstNumBands; i++) {
392             if (dstIsFloat) {
393                 dstScaleFactor[i] = (dstMaxVal[i] - dstMinVal[i]) / 65535.0f;
394                 dstUseMinVal[i] = dstMinVal[i];
395             } else {
396                 if (dstTransferType == DataBuffer.TYPE_SHORT) {
397                     dstScaleFactor[i] = 32767.0f / 65535.0f;
398                 } else {
399                     dstScaleFactor[i] =
400                         ((float) ((1 << dstSM.getSampleSize(i)) - 1)) /
401                         65535.0f;
402                 }
403                 dstUseMinVal[i] = 0.0f;
404             }
405         }
406         int ys = src.getMinY();
407         int yd = dst.getMinY();
408         int xs, xd;
409         float sample;
410         short[] srcLine = new short[w * srcNumBands];
411         short[] dstLine = new short[w * dstNumBands];
412         int idx;
413         try {
414             srcIL = new LCMSImageLayout(
415                     srcLine, srcLine.length/getNumInComponents(),
416                     LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
417                     LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
418 
419             dstIL = new LCMSImageLayout(
420                     dstLine, dstLine.length/getNumOutComponents(),
421                     LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
422                     LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
423         } catch (ImageLayoutException e) {
424             throw new CMMException("Unable to convert rasters");
425         }
426         // process each scanline
427         for (int y = 0; y < h; y++, ys++, yd++) {
428             // get src scanline
429             xs = src.getMinX();
430             idx = 0;
431             for (int x = 0; x < w; x++, xs++) {
432                 for (int i = 0; i < srcNumBands; i++) {
433                     sample = src.getSampleFloat(xs, ys, i);
434                     srcLine[idx++] = (short)
435                         ((sample - srcUseMinVal[i]) * srcScaleFactor[i] + 0.5f);
436                 }
437             }
438 
439             // color convert srcLine to dstLine
440             doTransform(srcIL, dstIL);
441 
442             // store dst scanline
443             xd = dst.getMinX();
444             idx = 0;
445             for (int x = 0; x < w; x++, xd++) {
446                 for (int i = 0; i < dstNumBands; i++) {
447                     sample = ((dstLine[idx++] & 0xffff) * dstScaleFactor[i]) +
448                              dstUseMinVal[i];
449                     dst.setSample(xd, yd, i, sample);
450                 }
451             }
452         }
453     }
454 
colorConvert(Raster src, WritableRaster dst)455     public void colorConvert(Raster src, WritableRaster dst) {
456 
457         LCMSImageLayout srcIL, dstIL;
458         dstIL = LCMSImageLayout.createImageLayout(dst);
459         if (dstIL != null) {
460             srcIL = LCMSImageLayout.createImageLayout(src);
461             if (srcIL != null) {
462                 doTransform(srcIL, dstIL);
463                 return;
464             }
465         }
466         // Can't pass src and dst directly to CMM, so process per scanline
467         SampleModel srcSM = src.getSampleModel();
468         SampleModel dstSM = dst.getSampleModel();
469         int srcTransferType = src.getTransferType();
470         int dstTransferType = dst.getTransferType();
471         int w = src.getWidth();
472         int h = src.getHeight();
473         int srcNumBands = src.getNumBands();
474         int dstNumBands = dst.getNumBands();
475         int precision = 8;
476         float maxNum = 255.0f;
477         for (int i = 0; i < srcNumBands; i++) {
478             if (srcSM.getSampleSize(i) > 8) {
479                  precision = 16;
480                  maxNum = 65535.0f;
481              }
482         }
483         for (int i = 0; i < dstNumBands; i++) {
484             if (dstSM.getSampleSize(i) > 8) {
485                  precision = 16;
486                  maxNum = 65535.0f;
487              }
488         }
489         float[] srcScaleFactor = new float[srcNumBands];
490         float[] dstScaleFactor = new float[dstNumBands];
491         for (int i = 0; i < srcNumBands; i++) {
492             if (srcTransferType == DataBuffer.TYPE_SHORT) {
493                 srcScaleFactor[i] = maxNum / 32767.0f;
494             } else {
495                 srcScaleFactor[i] = maxNum /
496                     ((float) ((1 << srcSM.getSampleSize(i)) - 1));
497             }
498         }
499         for (int i = 0; i < dstNumBands; i++) {
500             if (dstTransferType == DataBuffer.TYPE_SHORT) {
501                 dstScaleFactor[i] = 32767.0f / maxNum;
502             } else {
503                 dstScaleFactor[i] =
504                     ((float) ((1 << dstSM.getSampleSize(i)) - 1)) / maxNum;
505             }
506         }
507         int ys = src.getMinY();
508         int yd = dst.getMinY();
509         int xs, xd;
510         int sample;
511         if (precision == 8) {
512             byte[] srcLine = new byte[w * srcNumBands];
513             byte[] dstLine = new byte[w * dstNumBands];
514             int idx;
515             // TODO check for src npixels = dst npixels
516             try {
517                 srcIL = new LCMSImageLayout(
518                         srcLine, srcLine.length/getNumInComponents(),
519                         LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
520                         LCMSImageLayout.BYTES_SH(1), getNumInComponents());
521                 dstIL = new LCMSImageLayout(
522                         dstLine, dstLine.length/getNumOutComponents(),
523                         LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
524                         LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
525             } catch (ImageLayoutException e) {
526                 throw new CMMException("Unable to convert rasters");
527             }
528             // process each scanline
529             for (int y = 0; y < h; y++, ys++, yd++) {
530                 // get src scanline
531                 xs = src.getMinX();
532                 idx = 0;
533                 for (int x = 0; x < w; x++, xs++) {
534                     for (int i = 0; i < srcNumBands; i++) {
535                         sample = src.getSample(xs, ys, i);
536                         srcLine[idx++] = (byte)
537                             ((sample * srcScaleFactor[i]) + 0.5f);
538                     }
539                 }
540 
541                 // color convert srcLine to dstLine
542                 doTransform(srcIL, dstIL);
543 
544                 // store dst scanline
545                 xd = dst.getMinX();
546                 idx = 0;
547                 for (int x = 0; x < w; x++, xd++) {
548                     for (int i = 0; i < dstNumBands; i++) {
549                         sample = (int) (((dstLine[idx++] & 0xff) *
550                                          dstScaleFactor[i]) + 0.5f);
551                         dst.setSample(xd, yd, i, sample);
552                     }
553                 }
554             }
555         } else {
556             short[] srcLine = new short[w * srcNumBands];
557             short[] dstLine = new short[w * dstNumBands];
558             int idx;
559 
560             try {
561                 srcIL = new LCMSImageLayout(
562                         srcLine, srcLine.length/getNumInComponents(),
563                         LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
564                         LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
565 
566                 dstIL = new LCMSImageLayout(
567                         dstLine, dstLine.length/getNumOutComponents(),
568                         LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
569                         LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
570             } catch (ImageLayoutException e) {
571                 throw new CMMException("Unable to convert rasters");
572             }
573             // process each scanline
574             for (int y = 0; y < h; y++, ys++, yd++) {
575                 // get src scanline
576                 xs = src.getMinX();
577                 idx = 0;
578                 for (int x = 0; x < w; x++, xs++) {
579                     for (int i = 0; i < srcNumBands; i++) {
580                         sample = src.getSample(xs, ys, i);
581                         srcLine[idx++] = (short)
582                             ((sample * srcScaleFactor[i]) + 0.5f);
583                     }
584                 }
585 
586                 // color convert srcLine to dstLine
587                 doTransform(srcIL, dstIL);
588 
589                 // store dst scanline
590                 xd = dst.getMinX();
591                 idx = 0;
592                 for (int x = 0; x < w; x++, xd++) {
593                     for (int i = 0; i < dstNumBands; i++) {
594                         sample = (int) (((dstLine[idx++] & 0xffff) *
595                                          dstScaleFactor[i]) + 0.5f);
596                         dst.setSample(xd, yd, i, sample);
597                     }
598                 }
599             }
600         }
601     }
602 
603     /* convert an array of colors in short format */
604     /* each color is a contiguous set of array elements */
605     /* the number of colors is (size of the array) / (number of input/output
606        components */
colorConvert(short[] src, short[] dst)607     public short[] colorConvert(short[] src, short[] dst) {
608 
609         if (dst == null) {
610             dst = new short [(src.length/getNumInComponents())*getNumOutComponents()];
611         }
612 
613         try {
614             LCMSImageLayout srcIL = new LCMSImageLayout(
615                     src, src.length/getNumInComponents(),
616                     LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
617                     LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
618 
619             LCMSImageLayout dstIL = new LCMSImageLayout(
620                     dst, dst.length/getNumOutComponents(),
621                     LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
622                     LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
623 
624             doTransform(srcIL, dstIL);
625 
626             return dst;
627         } catch (ImageLayoutException e) {
628             throw new CMMException("Unable to convert data");
629         }
630     }
631 
colorConvert(byte[] src, byte[] dst)632     public byte[] colorConvert(byte[] src, byte[] dst) {
633         if (dst == null) {
634             dst = new byte [(src.length/getNumInComponents())*getNumOutComponents()];
635         }
636 
637         try {
638             LCMSImageLayout srcIL = new LCMSImageLayout(
639                     src, src.length/getNumInComponents(),
640                     LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
641                     LCMSImageLayout.BYTES_SH(1), getNumInComponents());
642 
643             LCMSImageLayout dstIL = new LCMSImageLayout(
644                     dst, dst.length/getNumOutComponents(),
645                     LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
646                     LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
647 
648             doTransform(srcIL, dstIL);
649 
650             return dst;
651         } catch (ImageLayoutException e) {
652             throw new CMMException("Unable to convert data");
653         }
654     }
655 }
656