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         dstIL = LCMSImageLayout.createImageLayout(dst);
 448         if (dstIL != null) {
 449             srcIL = LCMSImageLayout.createImageLayout(src);
 450             if (srcIL != null) {
 451                 doTransform(srcIL, dstIL);
 452                 return;
 453             }
 454         }
 455         // Can't pass src and dst directly to CMM, so process per scanline
 456         SampleModel srcSM = src.getSampleModel();
 457         SampleModel dstSM = dst.getSampleModel();
 458         int srcTransferType = src.getTransferType();
 459         int dstTransferType = dst.getTransferType();
 460         int w = src.getWidth();
 461         int h = src.getHeight();
 462         int srcNumBands = src.getNumBands();
 463         int dstNumBands = dst.getNumBands();
 464         int precision = 8;
 465         float maxNum = 255.0f;
 466         for (int i = 0; i < srcNumBands; i++) {
 467             if (srcSM.getSampleSize(i) > 8) {
 468                  precision = 16;
 469                  maxNum = 65535.0f;
 470              }
 471         }
 472         for (int i = 0; i < dstNumBands; i++) {
 473             if (dstSM.getSampleSize(i) > 8) {
 474                  precision = 16;
 475                  maxNum = 65535.0f;
 476              }
 477         }
 478         float[] srcScaleFactor = new float[srcNumBands];
 479         float[] dstScaleFactor = new float[dstNumBands];
 480         for (int i = 0; i < srcNumBands; i++) {
 481             if (srcTransferType == DataBuffer.TYPE_SHORT) {
 482                 srcScaleFactor[i] = maxNum / 32767.0f;
 483             } else {
 484                 srcScaleFactor[i] = maxNum /
 485                     ((float) ((1 << srcSM.getSampleSize(i)) - 1));
 486             }
 487         }
 488         for (int i = 0; i < dstNumBands; i++) {
 489             if (dstTransferType == DataBuffer.TYPE_SHORT) {
 490                 dstScaleFactor[i] = 32767.0f / maxNum;
 491             } else {
 492                 dstScaleFactor[i] =
 493                     ((float) ((1 << dstSM.getSampleSize(i)) - 1)) / maxNum;
 494             }
 495         }
 496         int ys = src.getMinY();
 497         int yd = dst.getMinY();
 498         int xs, xd;
 499         int sample;
 500         if (precision == 8) {
 501             byte[] srcLine = new byte[w * srcNumBands];
 502             byte[] dstLine = new byte[w * dstNumBands];
 503             int idx;
 504             // TODO check for src npixels = dst npixels
 505             srcIL = new LCMSImageLayout(
 506                 srcLine, srcLine.length/getNumInComponents(),
 507                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 508                 LCMSImageLayout.BYTES_SH(1), getNumInComponents());
 509             dstIL = new LCMSImageLayout(
 510                 dstLine, dstLine.length/getNumOutComponents(),
 511                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 512                 LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
 513 
 514             // process each scanline
 515             for (int y = 0; y < h; y++, ys++, yd++) {
 516                 // get src scanline
 517                 xs = src.getMinX();
 518                 idx = 0;
 519                 for (int x = 0; x < w; x++, xs++) {
 520                     for (int i = 0; i < srcNumBands; i++) {
 521                         sample = src.getSample(xs, ys, i);
 522                         srcLine[idx++] = (byte)
 523                             ((sample * srcScaleFactor[i]) + 0.5f);
 524                     }
 525                 }
 526 
 527                 // color convert srcLine to dstLine
 528                 doTransform(srcIL, dstIL);
 529 
 530                 // store dst scanline
 531                 xd = dst.getMinX();
 532                 idx = 0;
 533                 for (int x = 0; x < w; x++, xd++) {
 534                     for (int i = 0; i < dstNumBands; i++) {
 535                         sample = (int) (((dstLine[idx++] & 0xff) *
 536                                          dstScaleFactor[i]) + 0.5f);
 537                         dst.setSample(xd, yd, i, sample);
 538                     }
 539                 }
 540             }
 541         } else {
 542             short[] srcLine = new short[w * srcNumBands];
 543             short[] dstLine = new short[w * dstNumBands];
 544             int idx;
 545             srcIL = new LCMSImageLayout(
 546                 srcLine, srcLine.length/getNumInComponents(),
 547                 LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 548                 LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 549 
 550             dstIL = new LCMSImageLayout(
 551                 dstLine, dstLine.length/getNumOutComponents(),
 552                 LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 553                 LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 554 
 555             // process each scanline
 556             for (int y = 0; y < h; y++, ys++, yd++) {
 557                 // get src scanline
 558                 xs = src.getMinX();
 559                 idx = 0;
 560                 for (int x = 0; x < w; x++, xs++) {
 561                     for (int i = 0; i < srcNumBands; i++) {
 562                         sample = src.getSample(xs, ys, i);
 563                         srcLine[idx++] = (short)
 564                             ((sample * srcScaleFactor[i]) + 0.5f);
 565                     }
 566                 }
 567 
 568                 // color convert srcLine to dstLine
 569                 doTransform(srcIL, dstIL);
 570 
 571                 // store dst scanline
 572                 xd = dst.getMinX();
 573                 idx = 0;
 574                 for (int x = 0; x < w; x++, xd++) {
 575                     for (int i = 0; i < dstNumBands; i++) {
 576                         sample = (int) (((dstLine[idx++] & 0xffff) *
 577                                          dstScaleFactor[i]) + 0.5f);
 578                         dst.setSample(xd, yd, i, sample);
 579                     }
 580                 }
 581             }
 582         }
 583     }
 584 
 585     /* convert an array of colors in short format */
 586     /* each color is a contiguous set of array elements */
 587     /* the number of colors is (size of the array) / (number of input/output
 588        components */
 589     public short[] colorConvert(short[] src, short[] dst) {
 590 
 591         if (dst == null) {
 592             dst = new short [(src.length/getNumInComponents())*getNumOutComponents()];
 593         }
 594 
 595         LCMSImageLayout srcIL = new LCMSImageLayout(
 596             src, src.length/getNumInComponents(),
 597             LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 598             LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2);
 599 
 600         LCMSImageLayout dstIL = new LCMSImageLayout(
 601             dst, dst.length/getNumOutComponents(),
 602             LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 603             LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
 604 
 605         doTransform(srcIL, dstIL);
 606 
 607         return dst;
 608     }
 609 
 610     public byte[] colorConvert(byte[] src, byte[] dst) {
 611         if (dst == null) {
 612             dst = new byte [(src.length/getNumInComponents())*getNumOutComponents()];
 613         }
 614 
 615         LCMSImageLayout srcIL = new LCMSImageLayout(
 616             src, src.length/getNumInComponents(),
 617             LCMSImageLayout.CHANNELS_SH(getNumInComponents()) |
 618             LCMSImageLayout.BYTES_SH(1), getNumInComponents());
 619 
 620         LCMSImageLayout dstIL = new LCMSImageLayout(
 621             dst, dst.length/getNumOutComponents(),
 622             LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
 623             LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
 624 
 625         doTransform(srcIL, dstIL);
 626 
 627         return dst;
 628     }
 629 }