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         LCMSImageLayout srcIL, dstIL;
 165         
 166         dstIL = LCMSImageLayout.createImageLayout(dst);
 167         
 168         if (dstIL != null) {
 169             srcIL = LCMSImageLayout.createImageLayout(src);
 170             if (srcIL != null) {
 171                 doTransform(srcIL, dstIL);
 172                 return;
 173             }
 174         }
 175         
 176         Raster srcRas = src.getRaster();
 177         WritableRaster dstRas = dst.getRaster();
 178         ColorModel srcCM = src.getColorModel();
 179         ColorModel dstCM = dst.getColorModel();
 180         int w = src.getWidth();
 181         int h = src.getHeight();
 182         int srcNumComp = srcCM.getNumColorComponents();
 183         int dstNumComp = dstCM.getNumColorComponents();
 184         int precision = 8;
 185         float maxNum = 255.0f;
 186         for (int i = 0; i < srcNumComp; i++) {
 187             if (srcCM.getComponentSize(i) > 8) {
 188                  precision = 16;
 189                  maxNum = 65535.0f;
 190              }
 191         }
 192         for (int i = 0; i < dstNumComp; i++) {
 193             if (dstCM.getComponentSize(i) > 8) {
 194                  precision = 16;
 195                  maxNum = 65535.0f;
 196              }
 197         }
 198         float[] srcMinVal = new float[srcNumComp];
 199         float[] srcInvDiffMinMax = new float[srcNumComp];
 200         ColorSpace cs = srcCM.getColorSpace();
 201         for (int i = 0; i < srcNumComp; i++) {
 202             srcMinVal[i] = cs.getMinValue(i);
 203             srcInvDiffMinMax[i] = maxNum / (cs.getMaxValue(i) - srcMinVal[i]);
 204         }
 205         cs = dstCM.getColorSpace();
 206         float[] dstMinVal = new float[dstNumComp];
 207         float[] dstDiffMinMax = new float[dstNumComp];
 208         for (int i = 0; i < dstNumComp; i++) {
 209             dstMinVal[i] = cs.getMinValue(i);
 210             dstDiffMinMax[i] = (cs.getMaxValue(i) - dstMinVal[i]) / maxNum;
 211         }
 212         boolean dstHasAlpha = dstCM.hasAlpha();
 213         boolean needSrcAlpha = srcCM.hasAlpha() && dstHasAlpha;
 214         float[] dstColor;
 215         if (dstHasAlpha) {
 216             dstColor = new float[dstNumComp + 1];
 217         } else {
 218             dstColor = new float[dstNumComp];
 219         }
 220         if (precision == 8) {
 221             byte[] srcLine = new byte[w * srcNumComp];
 222             byte[] dstLine = new byte[w * dstNumComp];
 223             Object pixel;
 224             float[] color;
 225             float[] alpha = null;
 226             if (needSrcAlpha) {
 227                 alpha = new float[w];
 228             }
 229             int idx;
 230             // TODO check for src npixels = dst npixels
 231             srcIL = new LCMSImageLayout(
 232                 srcLine, srcLine.length/getNumInComponents(),
 233                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 234                 LCMSImageLayout.BYTES_SH(1), getNumInComponents());
 235             dstIL = new LCMSImageLayout(
 236                 dstLine, dstLine.length/getNumOutComponents(),
 237                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 238                 LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
 239             // process each scanline
 240             for (int y = 0; y < h; y++) {
 241                 // convert src scanline
 242                 pixel = null;
 243                 color = null;
 244                 idx = 0;
 245                 for (int x = 0; x < w; x++) {
 246                     pixel = srcRas.getDataElements(x, y, pixel);
 247                     color = srcCM.getNormalizedComponents(pixel, color, 0);
 248                     for (int i = 0; i < srcNumComp; i++) {
 249                         srcLine[idx++] = (byte)
 250                             ((color[i] - srcMinVal[i]) * srcInvDiffMinMax[i] +
 251                              0.5f);
 252                     }
 253                     if (needSrcAlpha) {
 254                         alpha[x] = color[srcNumComp];
 255                     }
 256                 }
 257                 // color convert srcLine to dstLine
 258                 doTransform(srcIL, dstIL);
 259 
 260                 // convert dst scanline
 261                 pixel = null;
 262                 idx = 0;
 263                 for (int x = 0; x < w; x++) {
 264                     for (int i = 0; i < dstNumComp; i++) {
 265                         dstColor[i] = ((float) (dstLine[idx++] & 0xff)) *
 266                                       dstDiffMinMax[i] + dstMinVal[i];
 267                     }
 268                     if (needSrcAlpha) {
 269                         dstColor[dstNumComp] = alpha[x];
 270                     } else if (dstHasAlpha) {
 271                         dstColor[dstNumComp] = 1.0f;
 272                     }
 273                     pixel = dstCM.getDataElements(dstColor, 0, pixel);
 274                     dstRas.setDataElements(x, y, pixel);
 275                 }
 276             }
 277         } else {
 278             short[] srcLine = new short[w * srcNumComp];
 279             short[] dstLine = new short[w * dstNumComp];
 280             Object pixel;
 281             float[] color;
 282             float[] alpha = null;
 283             if (needSrcAlpha) {
 284                 alpha = new float[w];
 285             }
 286             int idx;
 287             srcIL = new LCMSImageLayout(
 288                 srcLine, srcLine.length/getNumInComponents(),
 289                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 290                 LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 291 
 292             dstIL = new LCMSImageLayout(
 293                 dstLine, dstLine.length/getNumOutComponents(),
 294                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 295                 LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 296 
 297             // process each scanline
 298             for (int y = 0; y < h; y++) {
 299                 // convert src scanline
 300                 pixel = null;
 301                 color = null;
 302                 idx = 0;
 303                 for (int x = 0; x < w; x++) {
 304                     pixel = srcRas.getDataElements(x, y, pixel);
 305                     color = srcCM.getNormalizedComponents(pixel, color, 0);
 306                     for (int i = 0; i < srcNumComp; i++) {
 307                         srcLine[idx++] = (short)
 308                             ((color[i] - srcMinVal[i]) * srcInvDiffMinMax[i] +
 309                              0.5f);
 310                     }
 311                     if (needSrcAlpha) {
 312                         alpha[x] = color[srcNumComp];
 313                     }
 314                 }
 315                 // color convert srcLine to dstLine
 316                 doTransform(srcIL, dstIL);
 317 
 318                 // convert dst scanline
 319                 pixel = null;
 320                 idx = 0;
 321                 for (int x = 0; x < w; x++) {
 322                     for (int i = 0; i < dstNumComp; i++) {
 323                         dstColor[i] = ((float) (dstLine[idx++] & 0xffff)) *
 324                                       dstDiffMinMax[i] + dstMinVal[i];
 325                     }
 326                     if (needSrcAlpha) {
 327                         dstColor[dstNumComp] = alpha[x];
 328                     } else if (dstHasAlpha) {
 329                         dstColor[dstNumComp] = 1.0f;
 330                     }
 331                     pixel = dstCM.getDataElements(dstColor, 0, pixel);
 332                     dstRas.setDataElements(x, y, pixel);
 333                 }
 334             }
 335         }
 336     }
 337 
 338     public void colorConvert(Raster src, WritableRaster dst,
 339                              float[] srcMinVal, float[]srcMaxVal,
 340                              float[] dstMinVal, float[]dstMaxVal) {
 341         LCMSImageLayout srcIL, dstIL;
 342 
 343         // Can't pass src and dst directly to CMM, so process per scanline
 344         SampleModel srcSM = src.getSampleModel();
 345         SampleModel dstSM = dst.getSampleModel();
 346         int srcTransferType = src.getTransferType();
 347         int dstTransferType = dst.getTransferType();
 348         boolean srcIsFloat, dstIsFloat;
 349         if ((srcTransferType == DataBuffer.TYPE_FLOAT) ||
 350             (srcTransferType == DataBuffer.TYPE_DOUBLE)) {
 351             srcIsFloat = true;
 352         } else {
 353             srcIsFloat = false;
 354         }
 355         if ((dstTransferType == DataBuffer.TYPE_FLOAT) ||
 356             (dstTransferType == DataBuffer.TYPE_DOUBLE)) {
 357             dstIsFloat = true;
 358         } else {
 359             dstIsFloat = false;
 360         }
 361         int w = src.getWidth();
 362         int h = src.getHeight();
 363         int srcNumBands = src.getNumBands();
 364         int dstNumBands = dst.getNumBands();
 365         float[] srcScaleFactor = new float[srcNumBands];
 366         float[] dstScaleFactor = new float[dstNumBands];
 367         float[] srcUseMinVal = new float[srcNumBands];
 368         float[] dstUseMinVal = new float[dstNumBands];
 369         for (int i = 0; i < srcNumBands; i++) {
 370             if (srcIsFloat) {
 371                 srcScaleFactor[i] =  65535.0f / (srcMaxVal[i] - srcMinVal[i]);
 372                 srcUseMinVal[i] = srcMinVal[i];
 373             } else {
 374                 if (srcTransferType == DataBuffer.TYPE_SHORT) {
 375                     srcScaleFactor[i] = 65535.0f / 32767.0f;
 376                 } else {
 377                     srcScaleFactor[i] = 65535.0f /
 378                         ((float) ((1 << srcSM.getSampleSize(i)) - 1));
 379                 }
 380                 srcUseMinVal[i] = 0.0f;
 381             }
 382         }
 383         for (int i = 0; i < dstNumBands; i++) {
 384             if (dstIsFloat) {
 385                 dstScaleFactor[i] = (dstMaxVal[i] - dstMinVal[i]) / 65535.0f;
 386                 dstUseMinVal[i] = dstMinVal[i];
 387             } else {
 388                 if (dstTransferType == DataBuffer.TYPE_SHORT) {
 389                     dstScaleFactor[i] = 32767.0f / 65535.0f;
 390                 } else {
 391                     dstScaleFactor[i] =
 392                         ((float) ((1 << dstSM.getSampleSize(i)) - 1)) /
 393                         65535.0f;
 394                 }
 395                 dstUseMinVal[i] = 0.0f;
 396             }
 397         }
 398         int ys = src.getMinY();
 399         int yd = dst.getMinY();
 400         int xs, xd;
 401         float sample;
 402         short[] srcLine = new short[w * srcNumBands];
 403         short[] dstLine = new short[w * dstNumBands];
 404         int idx;
 405         srcIL = new LCMSImageLayout(
 406             srcLine, srcLine.length/getNumInComponents(),
 407             LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 408             LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 409 
 410         dstIL = new LCMSImageLayout(
 411             dstLine, dstLine.length/getNumOutComponents(),
 412             LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 413             LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 414 
 415         // process each scanline
 416         for (int y = 0; y < h; y++, ys++, yd++) {
 417             // get src scanline
 418             xs = src.getMinX();
 419             idx = 0;
 420             for (int x = 0; x < w; x++, xs++) {
 421                 for (int i = 0; i < srcNumBands; i++) {
 422                     sample = src.getSampleFloat(xs, ys, i);
 423                     srcLine[idx++] = (short)
 424                         ((sample - srcUseMinVal[i]) * srcScaleFactor[i] + 0.5f);
 425                 }
 426             }
 427 
 428             // color convert srcLine to dstLine
 429             doTransform(srcIL, dstIL);
 430 
 431             // store dst scanline
 432             xd = dst.getMinX();
 433             idx = 0;
 434             for (int x = 0; x < w; x++, xd++) {
 435                 for (int i = 0; i < dstNumBands; i++) {
 436                     sample = ((dstLine[idx++] & 0xffff) * dstScaleFactor[i]) +
 437                              dstUseMinVal[i];
 438                     dst.setSample(xd, yd, i, sample);
 439                 }
 440             }
 441         }
 442     }
 443 
 444     public void colorConvert(Raster src, WritableRaster dst) {
 445 
 446         LCMSImageLayout srcIL, dstIL;
 447         // Can't pass src and dst directly to CMM, so process per scanline
 448         SampleModel srcSM = src.getSampleModel();
 449         SampleModel dstSM = dst.getSampleModel();
 450         int srcTransferType = src.getTransferType();
 451         int dstTransferType = dst.getTransferType();
 452         int w = src.getWidth();
 453         int h = src.getHeight();
 454         int srcNumBands = src.getNumBands();
 455         int dstNumBands = dst.getNumBands();
 456         int precision = 8;
 457         float maxNum = 255.0f;
 458         for (int i = 0; i < srcNumBands; i++) {
 459             if (srcSM.getSampleSize(i) > 8) {
 460                  precision = 16;
 461                  maxNum = 65535.0f;
 462              }
 463         }
 464         for (int i = 0; i < dstNumBands; i++) {
 465             if (dstSM.getSampleSize(i) > 8) {
 466                  precision = 16;
 467                  maxNum = 65535.0f;
 468              }
 469         }
 470         float[] srcScaleFactor = new float[srcNumBands];
 471         float[] dstScaleFactor = new float[dstNumBands];
 472         for (int i = 0; i < srcNumBands; i++) {
 473             if (srcTransferType == DataBuffer.TYPE_SHORT) {
 474                 srcScaleFactor[i] = maxNum / 32767.0f;
 475             } else {
 476                 srcScaleFactor[i] = maxNum /
 477                     ((float) ((1 << srcSM.getSampleSize(i)) - 1));
 478             }
 479         }
 480         for (int i = 0; i < dstNumBands; i++) {
 481             if (dstTransferType == DataBuffer.TYPE_SHORT) {
 482                 dstScaleFactor[i] = 32767.0f / maxNum;
 483             } else {
 484                 dstScaleFactor[i] =
 485                     ((float) ((1 << dstSM.getSampleSize(i)) - 1)) / maxNum;
 486             }
 487         }
 488         int ys = src.getMinY();
 489         int yd = dst.getMinY();
 490         int xs, xd;
 491         int sample;
 492         if (precision == 8) {
 493             byte[] srcLine = new byte[w * srcNumBands];
 494             byte[] dstLine = new byte[w * dstNumBands];
 495             int idx;
 496             // TODO check for src npixels = dst npixels
 497             srcIL = new LCMSImageLayout(
 498                 srcLine, srcLine.length/getNumInComponents(),
 499                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 500                 LCMSImageLayout.BYTES_SH(1), getNumInComponents());
 501             dstIL = new LCMSImageLayout(
 502                 dstLine, dstLine.length/getNumOutComponents(),
 503                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 504                 LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
 505 
 506             // process each scanline
 507             for (int y = 0; y < h; y++, ys++, yd++) {
 508                 // get src scanline
 509                 xs = src.getMinX();
 510                 idx = 0;
 511                 for (int x = 0; x < w; x++, xs++) {
 512                     for (int i = 0; i < srcNumBands; i++) {
 513                         sample = src.getSample(xs, ys, i);
 514                         srcLine[idx++] = (byte)
 515                             ((sample * srcScaleFactor[i]) + 0.5f);
 516                     }
 517                 }
 518 
 519                 // color convert srcLine to dstLine
 520                 doTransform(srcIL, dstIL);
 521 
 522                 // store dst scanline
 523                 xd = dst.getMinX();
 524                 idx = 0;
 525                 for (int x = 0; x < w; x++, xd++) {
 526                     for (int i = 0; i < dstNumBands; i++) {
 527                         sample = (int) (((dstLine[idx++] & 0xff) *
 528                                          dstScaleFactor[i]) + 0.5f);
 529                         dst.setSample(xd, yd, i, sample);
 530                     }
 531                 }
 532             }
 533         } else {
 534             short[] srcLine = new short[w * srcNumBands];
 535             short[] dstLine = new short[w * dstNumBands];
 536             int idx;
 537             srcIL = new LCMSImageLayout(
 538                 srcLine, srcLine.length/getNumInComponents(),
 539                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 540                 LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 541 
 542             dstIL = new LCMSImageLayout(
 543                 dstLine, dstLine.length/getNumOutComponents(),
 544                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 545                 LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 546 
 547             // process each scanline
 548             for (int y = 0; y < h; y++, ys++, yd++) {
 549                 // get src scanline
 550                 xs = src.getMinX();
 551                 idx = 0;
 552                 for (int x = 0; x < w; x++, xs++) {
 553                     for (int i = 0; i < srcNumBands; i++) {
 554                         sample = src.getSample(xs, ys, i);
 555                         srcLine[idx++] = (short)
 556                             ((sample * srcScaleFactor[i]) + 0.5f);
 557                     }
 558                 }
 559 
 560                 // color convert srcLine to dstLine
 561                 doTransform(srcIL, dstIL);
 562 
 563                 // store dst scanline
 564                 xd = dst.getMinX();
 565                 idx = 0;
 566                 for (int x = 0; x < w; x++, xd++) {
 567                     for (int i = 0; i < dstNumBands; i++) {
 568                         sample = (int) (((dstLine[idx++] & 0xffff) *
 569                                          dstScaleFactor[i]) + 0.5f);
 570                         dst.setSample(xd, yd, i, sample);
 571                     }
 572                 }
 573             }
 574         }
 575     }
 576 
 577     /* convert an array of colors in short format */
 578     /* each color is a contiguous set of array elements */
 579     /* the number of colors is (size of the array) / (number of input/output
 580        components */
 581     public short[] colorConvert(short[] src, short[] dst) {
 582 
 583         if (dst == null) {
 584             dst = new short [(src.length/getNumInComponents())*getNumOutComponents()];
 585         }
 586 
 587         LCMSImageLayout srcIL = new LCMSImageLayout(
 588             src, src.length/getNumInComponents(),
 589             LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 590             LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 591 
 592         LCMSImageLayout dstIL = new LCMSImageLayout(
 593             dst, dst.length/getNumOutComponents(),
 594             LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 595             LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 596 
 597         doTransform(srcIL, dstIL);
 598 
 599         return dst;
 600     }
 601 
 602     public byte[] colorConvert(byte[] src, byte[] dst) {
 603         if (dst == null) {
 604             dst = new byte [(src.length/getNumInComponents())*getNumOutComponents()];
 605         }
 606 
 607         LCMSImageLayout srcIL = new LCMSImageLayout(
 608             src, src.length/getNumInComponents(),
 609             LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 610             LCMSImageLayout.BYTES_SH(1), getNumInComponents());
 611 
 612         LCMSImageLayout dstIL = new LCMSImageLayout(
 613             dst, dst.length/getNumOutComponents(),
 614             LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 615             LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
 616 
 617         doTransform(srcIL, dstIL);
 618 
 619         return dst;
 620     }
 621 }