1 /*
   2  * Copyright (c) 2007, 2010, 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.ProfileDataException;
  40 import java.awt.color.CMMException;
  41 import java.awt.color.ColorSpace;
  42 import java.awt.image.BufferedImage;
  43 import java.awt.image.Raster;
  44 import java.awt.image.WritableRaster;
  45 import java.awt.image.ColorModel;
  46 import java.awt.image.DirectColorModel;
  47 import java.awt.image.ComponentColorModel;
  48 import java.awt.image.SampleModel;
  49 import java.awt.image.DataBuffer;
  50 import java.awt.image.SinglePixelPackedSampleModel;
  51 import java.awt.image.ComponentSampleModel;
  52 import sun.java2d.cmm.*;
  53 import sun.java2d.cmm.lcms.*;
  54 
  55 
  56 public class LCMSTransform implements ColorTransform {
  57     long ID;
  58     private int inFormatter = 0;
  59     private boolean isInIntPacked = false;
  60     private int outFormatter = 0;
  61     private boolean isOutIntPacked = false;
  62 
  63     ICC_Profile[] profiles;
  64     long [] profileIDs;
  65     int renderType;
  66     int transformType;
  67 
  68     private int numInComponents = -1;
  69     private int numOutComponents = -1;
  70 
  71     private Object disposerReferent = new Object();
  72 
  73     /* the class initializer */
  74     static {
  75         if (ProfileDeferralMgr.deferring) {
  76             ProfileDeferralMgr.activateProfiles();
  77         }
  78     }
  79 
  80     public LCMSTransform(ICC_Profile profile, int renderType,
  81                          int transformType)
  82     {
  83         /* Actually, it is not a complete transform but just part of it */
  84         profiles = new ICC_Profile[1];
  85         profiles[0] = profile;
  86         profileIDs = new long[1];
  87         profileIDs[0] = LCMS.getProfileID(profile);
  88         this.renderType = (renderType == ColorTransform.Any)?
  89                               ICC_Profile.icPerceptual : renderType;
  90         this.transformType = transformType;
  91 
  92         /* Note that ICC_Profile.getNumComponents() is quite expensive
  93          * (it may results in a reading of the profile header).
  94          * So, here we cache the number of components of input and
  95          * output profiles for further usage.
  96          */
  97         numInComponents = profiles[0].getNumComponents();
  98         numOutComponents = profiles[profiles.length - 1].getNumComponents();
  99     }
 100 
 101     public LCMSTransform (ColorTransform[] transforms) {
 102         int size = 0;
 103         for (int i=0; i < transforms.length; i++) {
 104             size+=((LCMSTransform)transforms[i]).profiles.length;
 105         }
 106         profiles = new ICC_Profile[size];
 107         profileIDs = new long[size];
 108         int j = 0;
 109         for (int i=0; i < transforms.length; i++) {
 110             LCMSTransform curTrans = (LCMSTransform)transforms[i];
 111             System.arraycopy(curTrans.profiles, 0, profiles, j,
 112                              curTrans.profiles.length);
 113             System.arraycopy(curTrans.profileIDs, 0, profileIDs, j,
 114                              curTrans.profileIDs.length);
 115             j += curTrans.profiles.length;
 116         }
 117         renderType = ((LCMSTransform)transforms[0]).renderType;
 118 
 119         /* Note that ICC_Profile.getNumComponents() is quite expensive
 120          * (it may results in a reading of the profile header).
 121          * So, here we cache the number of components of input and
 122          * output profiles for further usage.
 123          */
 124         numInComponents = profiles[0].getNumComponents();
 125         numOutComponents = profiles[profiles.length - 1].getNumComponents();
 126     }
 127 
 128     public int getNumInComponents() {
 129         return numInComponents;
 130     }
 131 
 132     public int getNumOutComponents() {
 133         return numOutComponents;
 134     }
 135 
 136     private synchronized void doTransform(LCMSImageLayout in,
 137                                           LCMSImageLayout out) {
 138         // update native transfrom if needed
 139         if (ID == 0L ||
 140             inFormatter != in.pixelType || isInIntPacked != in.isIntPacked ||
 141             outFormatter != out.pixelType || isOutIntPacked != out.isIntPacked)
 142         {
 143 
 144             if (ID != 0L) {
 145                 // Disposer will destroy forgotten transform
 146                 disposerReferent = new Object();
 147             }
 148             inFormatter = in.pixelType;
 149             isInIntPacked = in.isIntPacked;
 150 
 151             outFormatter = out.pixelType;
 152             isOutIntPacked = out.isIntPacked;
 153 
 154             ID = LCMS.createNativeTransform(profileIDs, renderType,
 155                                             inFormatter, isInIntPacked,
 156                                             outFormatter, isOutIntPacked,
 157                                             disposerReferent);
 158         }
 159 
 160         LCMS.colorConvert(this, in, out);
 161     }
 162 
 163     public void colorConvert(BufferedImage src, BufferedImage dst) {
 164         if (LCMSImageLayout.isSupported(src) &&
 165             LCMSImageLayout.isSupported(dst))
 166         {
 167             doTransform(new LCMSImageLayout(src), new LCMSImageLayout(dst));
 168             return;
 169         }
 170         LCMSImageLayout srcIL, dstIL;
 171         Raster srcRas = src.getRaster();
 172         WritableRaster dstRas = dst.getRaster();
 173         ColorModel srcCM = src.getColorModel();
 174         ColorModel dstCM = dst.getColorModel();
 175         int w = src.getWidth();
 176         int h = src.getHeight();
 177         int srcNumComp = srcCM.getNumColorComponents();
 178         int dstNumComp = dstCM.getNumColorComponents();
 179         int precision = 8;
 180         float maxNum = 255.0f;
 181         for (int i = 0; i < srcNumComp; i++) {
 182             if (srcCM.getComponentSize(i) > 8) {
 183                  precision = 16;
 184                  maxNum = 65535.0f;
 185              }
 186         }
 187         for (int i = 0; i < dstNumComp; i++) {
 188             if (dstCM.getComponentSize(i) > 8) {
 189                  precision = 16;
 190                  maxNum = 65535.0f;
 191              }
 192         }
 193         float[] srcMinVal = new float[srcNumComp];
 194         float[] srcInvDiffMinMax = new float[srcNumComp];
 195         ColorSpace cs = srcCM.getColorSpace();
 196         for (int i = 0; i < srcNumComp; i++) {
 197             srcMinVal[i] = cs.getMinValue(i);
 198             srcInvDiffMinMax[i] = maxNum / (cs.getMaxValue(i) - srcMinVal[i]);
 199         }
 200         cs = dstCM.getColorSpace();
 201         float[] dstMinVal = new float[dstNumComp];
 202         float[] dstDiffMinMax = new float[dstNumComp];
 203         for (int i = 0; i < dstNumComp; i++) {
 204             dstMinVal[i] = cs.getMinValue(i);
 205             dstDiffMinMax[i] = (cs.getMaxValue(i) - dstMinVal[i]) / maxNum;
 206         }
 207         boolean dstHasAlpha = dstCM.hasAlpha();
 208         boolean needSrcAlpha = srcCM.hasAlpha() && dstHasAlpha;
 209         float[] dstColor;
 210         if (dstHasAlpha) {
 211             dstColor = new float[dstNumComp + 1];
 212         } else {
 213             dstColor = new float[dstNumComp];
 214         }
 215         if (precision == 8) {
 216             byte[] srcLine = new byte[w * srcNumComp];
 217             byte[] dstLine = new byte[w * dstNumComp];
 218             Object pixel;
 219             float[] color;
 220             float[] alpha = null;
 221             if (needSrcAlpha) {
 222                 alpha = new float[w];
 223             }
 224             int idx;
 225             // TODO check for src npixels = dst npixels
 226             srcIL = new LCMSImageLayout(
 227                 srcLine, srcLine.length/getNumInComponents(),
 228                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 229                 LCMSImageLayout.BYTES_SH(1), getNumInComponents());
 230             dstIL = new LCMSImageLayout(
 231                 dstLine, dstLine.length/getNumOutComponents(),
 232                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 233                 LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
 234             // process each scanline
 235             for (int y = 0; y < h; y++) {
 236                 // convert src scanline
 237                 pixel = null;
 238                 color = null;
 239                 idx = 0;
 240                 for (int x = 0; x < w; x++) {
 241                     pixel = srcRas.getDataElements(x, y, pixel);
 242                     color = srcCM.getNormalizedComponents(pixel, color, 0);
 243                     for (int i = 0; i < srcNumComp; i++) {
 244                         srcLine[idx++] = (byte)
 245                             ((color[i] - srcMinVal[i]) * srcInvDiffMinMax[i] +
 246                              0.5f);
 247                     }
 248                     if (needSrcAlpha) {
 249                         alpha[x] = color[srcNumComp];
 250                     }
 251                 }
 252                 // color convert srcLine to dstLine
 253                 doTransform(srcIL, dstIL);
 254 
 255                 // convert dst scanline
 256                 pixel = null;
 257                 idx = 0;
 258                 for (int x = 0; x < w; x++) {
 259                     for (int i = 0; i < dstNumComp; i++) {
 260                         dstColor[i] = ((float) (dstLine[idx++] & 0xff)) *
 261                                       dstDiffMinMax[i] + dstMinVal[i];
 262                     }
 263                     if (needSrcAlpha) {
 264                         dstColor[dstNumComp] = alpha[x];
 265                     } else if (dstHasAlpha) {
 266                         dstColor[dstNumComp] = 1.0f;
 267                     }
 268                     pixel = dstCM.getDataElements(dstColor, 0, pixel);
 269                     dstRas.setDataElements(x, y, pixel);
 270                 }
 271             }
 272         } else {
 273             short[] srcLine = new short[w * srcNumComp];
 274             short[] dstLine = new short[w * dstNumComp];
 275             Object pixel;
 276             float[] color;
 277             float[] alpha = null;
 278             if (needSrcAlpha) {
 279                 alpha = new float[w];
 280             }
 281             int idx;
 282             srcIL = new LCMSImageLayout(
 283                 srcLine, srcLine.length/getNumInComponents(),
 284                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 285                 LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 286 
 287             dstIL = new LCMSImageLayout(
 288                 dstLine, dstLine.length/getNumOutComponents(),
 289                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 290                 LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 291 
 292             // process each scanline
 293             for (int y = 0; y < h; y++) {
 294                 // convert src scanline
 295                 pixel = null;
 296                 color = null;
 297                 idx = 0;
 298                 for (int x = 0; x < w; x++) {
 299                     pixel = srcRas.getDataElements(x, y, pixel);
 300                     color = srcCM.getNormalizedComponents(pixel, color, 0);
 301                     for (int i = 0; i < srcNumComp; i++) {
 302                         srcLine[idx++] = (short)
 303                             ((color[i] - srcMinVal[i]) * srcInvDiffMinMax[i] +
 304                              0.5f);
 305                     }
 306                     if (needSrcAlpha) {
 307                         alpha[x] = color[srcNumComp];
 308                     }
 309                 }
 310                 // color convert srcLine to dstLine
 311                 doTransform(srcIL, dstIL);
 312 
 313                 // convert dst scanline
 314                 pixel = null;
 315                 idx = 0;
 316                 for (int x = 0; x < w; x++) {
 317                     for (int i = 0; i < dstNumComp; i++) {
 318                         dstColor[i] = ((float) (dstLine[idx++] & 0xffff)) *
 319                                       dstDiffMinMax[i] + dstMinVal[i];
 320                     }
 321                     if (needSrcAlpha) {
 322                         dstColor[dstNumComp] = alpha[x];
 323                     } else if (dstHasAlpha) {
 324                         dstColor[dstNumComp] = 1.0f;
 325                     }
 326                     pixel = dstCM.getDataElements(dstColor, 0, pixel);
 327                     dstRas.setDataElements(x, y, pixel);
 328                 }
 329             }
 330         }
 331     }
 332 
 333     public void colorConvert(Raster src, WritableRaster dst,
 334                              float[] srcMinVal, float[]srcMaxVal,
 335                              float[] dstMinVal, float[]dstMaxVal) {
 336         LCMSImageLayout srcIL, dstIL;
 337 
 338         // Can't pass src and dst directly to CMM, so process per scanline
 339         SampleModel srcSM = src.getSampleModel();
 340         SampleModel dstSM = dst.getSampleModel();
 341         int srcTransferType = src.getTransferType();
 342         int dstTransferType = dst.getTransferType();
 343         boolean srcIsFloat, dstIsFloat;
 344         if ((srcTransferType == DataBuffer.TYPE_FLOAT) ||
 345             (srcTransferType == DataBuffer.TYPE_DOUBLE)) {
 346             srcIsFloat = true;
 347         } else {
 348             srcIsFloat = false;
 349         }
 350         if ((dstTransferType == DataBuffer.TYPE_FLOAT) ||
 351             (dstTransferType == DataBuffer.TYPE_DOUBLE)) {
 352             dstIsFloat = true;
 353         } else {
 354             dstIsFloat = false;
 355         }
 356         int w = src.getWidth();
 357         int h = src.getHeight();
 358         int srcNumBands = src.getNumBands();
 359         int dstNumBands = dst.getNumBands();
 360         float[] srcScaleFactor = new float[srcNumBands];
 361         float[] dstScaleFactor = new float[dstNumBands];
 362         float[] srcUseMinVal = new float[srcNumBands];
 363         float[] dstUseMinVal = new float[dstNumBands];
 364         for (int i = 0; i < srcNumBands; i++) {
 365             if (srcIsFloat) {
 366                 srcScaleFactor[i] =  65535.0f / (srcMaxVal[i] - srcMinVal[i]);
 367                 srcUseMinVal[i] = srcMinVal[i];
 368             } else {
 369                 if (srcTransferType == DataBuffer.TYPE_SHORT) {
 370                     srcScaleFactor[i] = 65535.0f / 32767.0f;
 371                 } else {
 372                     srcScaleFactor[i] = 65535.0f /
 373                         ((float) ((1 << srcSM.getSampleSize(i)) - 1));
 374                 }
 375                 srcUseMinVal[i] = 0.0f;
 376             }
 377         }
 378         for (int i = 0; i < dstNumBands; i++) {
 379             if (dstIsFloat) {
 380                 dstScaleFactor[i] = (dstMaxVal[i] - dstMinVal[i]) / 65535.0f;
 381                 dstUseMinVal[i] = dstMinVal[i];
 382             } else {
 383                 if (dstTransferType == DataBuffer.TYPE_SHORT) {
 384                     dstScaleFactor[i] = 32767.0f / 65535.0f;
 385                 } else {
 386                     dstScaleFactor[i] =
 387                         ((float) ((1 << dstSM.getSampleSize(i)) - 1)) /
 388                         65535.0f;
 389                 }
 390                 dstUseMinVal[i] = 0.0f;
 391             }
 392         }
 393         int ys = src.getMinY();
 394         int yd = dst.getMinY();
 395         int xs, xd;
 396         float sample;
 397         short[] srcLine = new short[w * srcNumBands];
 398         short[] dstLine = new short[w * dstNumBands];
 399         int idx;
 400         srcIL = new LCMSImageLayout(
 401             srcLine, srcLine.length/getNumInComponents(),
 402             LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 403             LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 404 
 405         dstIL = new LCMSImageLayout(
 406             dstLine, dstLine.length/getNumOutComponents(),
 407             LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 408             LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 409 
 410         // process each scanline
 411         for (int y = 0; y < h; y++, ys++, yd++) {
 412             // get src scanline
 413             xs = src.getMinX();
 414             idx = 0;
 415             for (int x = 0; x < w; x++, xs++) {
 416                 for (int i = 0; i < srcNumBands; i++) {
 417                     sample = src.getSampleFloat(xs, ys, i);
 418                     srcLine[idx++] = (short)
 419                         ((sample - srcUseMinVal[i]) * srcScaleFactor[i] + 0.5f);
 420                 }
 421             }
 422 
 423             // color convert srcLine to dstLine
 424             doTransform(srcIL, dstIL);
 425 
 426             // store dst scanline
 427             xd = dst.getMinX();
 428             idx = 0;
 429             for (int x = 0; x < w; x++, xd++) {
 430                 for (int i = 0; i < dstNumBands; i++) {
 431                     sample = ((dstLine[idx++] & 0xffff) * dstScaleFactor[i]) +
 432                              dstUseMinVal[i];
 433                     dst.setSample(xd, yd, i, sample);
 434                 }
 435             }
 436         }
 437     }
 438 
 439     public void colorConvert(Raster src, WritableRaster dst) {
 440 
 441         LCMSImageLayout srcIL, dstIL;
 442         // Can't pass src and dst directly to CMM, so process per scanline
 443         SampleModel srcSM = src.getSampleModel();
 444         SampleModel dstSM = dst.getSampleModel();
 445         int srcTransferType = src.getTransferType();
 446         int dstTransferType = dst.getTransferType();
 447         int w = src.getWidth();
 448         int h = src.getHeight();
 449         int srcNumBands = src.getNumBands();
 450         int dstNumBands = dst.getNumBands();
 451         int precision = 8;
 452         float maxNum = 255.0f;
 453         for (int i = 0; i < srcNumBands; i++) {
 454             if (srcSM.getSampleSize(i) > 8) {
 455                  precision = 16;
 456                  maxNum = 65535.0f;
 457              }
 458         }
 459         for (int i = 0; i < dstNumBands; i++) {
 460             if (dstSM.getSampleSize(i) > 8) {
 461                  precision = 16;
 462                  maxNum = 65535.0f;
 463              }
 464         }
 465         float[] srcScaleFactor = new float[srcNumBands];
 466         float[] dstScaleFactor = new float[dstNumBands];
 467         for (int i = 0; i < srcNumBands; i++) {
 468             if (srcTransferType == DataBuffer.TYPE_SHORT) {
 469                 srcScaleFactor[i] = maxNum / 32767.0f;
 470             } else {
 471                 srcScaleFactor[i] = maxNum /
 472                     ((float) ((1 << srcSM.getSampleSize(i)) - 1));
 473             }
 474         }
 475         for (int i = 0; i < dstNumBands; i++) {
 476             if (dstTransferType == DataBuffer.TYPE_SHORT) {
 477                 dstScaleFactor[i] = 32767.0f / maxNum;
 478             } else {
 479                 dstScaleFactor[i] =
 480                     ((float) ((1 << dstSM.getSampleSize(i)) - 1)) / maxNum;
 481             }
 482         }
 483         int ys = src.getMinY();
 484         int yd = dst.getMinY();
 485         int xs, xd;
 486         int sample;
 487         if (precision == 8) {
 488             byte[] srcLine = new byte[w * srcNumBands];
 489             byte[] dstLine = new byte[w * dstNumBands];
 490             int idx;
 491             // TODO check for src npixels = dst npixels
 492             srcIL = new LCMSImageLayout(
 493                 srcLine, srcLine.length/getNumInComponents(),
 494                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 495                 LCMSImageLayout.BYTES_SH(1), getNumInComponents());
 496             dstIL = new LCMSImageLayout(
 497                 dstLine, dstLine.length/getNumOutComponents(),
 498                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 499                 LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
 500 
 501             // process each scanline
 502             for (int y = 0; y < h; y++, ys++, yd++) {
 503                 // get src scanline
 504                 xs = src.getMinX();
 505                 idx = 0;
 506                 for (int x = 0; x < w; x++, xs++) {
 507                     for (int i = 0; i < srcNumBands; i++) {
 508                         sample = src.getSample(xs, ys, i);
 509                         srcLine[idx++] = (byte)
 510                             ((sample * srcScaleFactor[i]) + 0.5f);
 511                     }
 512                 }
 513 
 514                 // color convert srcLine to dstLine
 515                 doTransform(srcIL, dstIL);
 516 
 517                 // store dst scanline
 518                 xd = dst.getMinX();
 519                 idx = 0;
 520                 for (int x = 0; x < w; x++, xd++) {
 521                     for (int i = 0; i < dstNumBands; i++) {
 522                         sample = (int) (((dstLine[idx++] & 0xff) *
 523                                          dstScaleFactor[i]) + 0.5f);
 524                         dst.setSample(xd, yd, i, sample);
 525                     }
 526                 }
 527             }
 528         } else {
 529             short[] srcLine = new short[w * srcNumBands];
 530             short[] dstLine = new short[w * dstNumBands];
 531             int idx;
 532             srcIL = new LCMSImageLayout(
 533                 srcLine, srcLine.length/getNumInComponents(),
 534                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 535                 LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 536 
 537             dstIL = new LCMSImageLayout(
 538                 dstLine, dstLine.length/getNumOutComponents(),
 539                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 540                 LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 541 
 542             // process each scanline
 543             for (int y = 0; y < h; y++, ys++, yd++) {
 544                 // get src scanline
 545                 xs = src.getMinX();
 546                 idx = 0;
 547                 for (int x = 0; x < w; x++, xs++) {
 548                     for (int i = 0; i < srcNumBands; i++) {
 549                         sample = src.getSample(xs, ys, i);
 550                         srcLine[idx++] = (short)
 551                             ((sample * srcScaleFactor[i]) + 0.5f);
 552                     }
 553                 }
 554 
 555                 // color convert srcLine to dstLine
 556                 doTransform(srcIL, dstIL);
 557 
 558                 // store dst scanline
 559                 xd = dst.getMinX();
 560                 idx = 0;
 561                 for (int x = 0; x < w; x++, xd++) {
 562                     for (int i = 0; i < dstNumBands; i++) {
 563                         sample = (int) (((dstLine[idx++] & 0xffff) *
 564                                          dstScaleFactor[i]) + 0.5f);
 565                         dst.setSample(xd, yd, i, sample);
 566                     }
 567                 }
 568             }
 569         }
 570     }
 571 
 572     /* convert an array of colors in short format */
 573     /* each color is a contiguous set of array elements */
 574     /* the number of colors is (size of the array) / (number of input/output
 575        components */
 576     public short[] colorConvert(short[] src, short[] dst) {
 577 
 578         if (dst == null) {
 579             dst = new short [(src.length/getNumInComponents())*getNumOutComponents()];
 580         }
 581 
 582         LCMSImageLayout srcIL = new LCMSImageLayout(
 583             src, src.length/getNumInComponents(),
 584             LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 585             LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 586 
 587         LCMSImageLayout dstIL = new LCMSImageLayout(
 588             dst, dst.length/getNumOutComponents(),
 589             LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 590             LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 591 
 592         doTransform(srcIL, dstIL);
 593 
 594         return dst;
 595     }
 596 
 597     public byte[] colorConvert(byte[] src, byte[] dst) {
 598         if (dst == null) {
 599             dst = new byte [(src.length/getNumInComponents())*getNumOutComponents()];
 600         }
 601 
 602         LCMSImageLayout srcIL = new LCMSImageLayout(
 603             src, src.length/getNumInComponents(),
 604             LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 605             LCMSImageLayout.BYTES_SH(1), getNumInComponents());
 606 
 607         LCMSImageLayout dstIL = new LCMSImageLayout(
 608             dst, dst.length/getNumOutComponents(),
 609             LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 610             LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
 611 
 612         doTransform(srcIL, dstIL);
 613 
 614         return dst;
 615     }
 616 }