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