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 }