1 /*
   2  * Copyright (c) 2007, 2011, 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 #include <stdio.h>
  27 #include <stdlib.h>
  28 #include <memory.h>
  29 #include "sun_java2d_cmm_lcms_LCMS.h"
  30 #include "jni_util.h"
  31 #include "Trace.h"
  32 #include "Disposer.h"
  33 #include "lcms2.h"
  34 
  35 
  36 #define ALIGNLONG(x) (((x)+3) & ~(3))         // Aligns to DWORD boundary
  37 
  38 #ifdef USE_BIG_ENDIAN
  39 #define AdjustEndianess32(a)
  40 #else
  41 
  42 static
  43 void AdjustEndianess32(cmsUInt8Number *pByte)
  44 {
  45     cmsUInt8Number temp1;
  46     cmsUInt8Number temp2;
  47 
  48     temp1 = *pByte++;
  49     temp2 = *pByte++;
  50     *(pByte-1) = *pByte;
  51     *pByte++ = temp2;
  52     *(pByte-3) = *pByte;
  53     *pByte = temp1;
  54 }
  55 
  56 #endif
  57 
  58 // Transports to properly encoded values - note that icc profiles does use
  59 // big endian notation.
  60 
  61 static
  62 cmsInt32Number TransportValue32(cmsInt32Number Value)
  63 {
  64     cmsInt32Number Temp = Value;
  65 
  66     AdjustEndianess32((cmsUInt8Number*) &Temp);
  67     return Temp;
  68 }
  69 
  70 #define SigMake(a,b,c,d) \
  71                     ( ( ((int) ((unsigned char) (a))) << 24) | \
  72                       ( ((int) ((unsigned char) (b))) << 16) | \
  73                       ( ((int) ((unsigned char) (c))) <<  8) | \
  74                           (int) ((unsigned char) (d)))
  75 
  76 #define TagIdConst(a, b, c, d) \
  77                 ((int) SigMake ((a), (b), (c), (d)))
  78 
  79 #define SigHead TagIdConst('h','e','a','d')
  80 
  81 #define DT_BYTE     0
  82 #define DT_SHORT    1
  83 #define DT_INT      2
  84 #define DT_DOUBLE   3
  85 
  86 /* Default temp profile list size */
  87 #define DF_ICC_BUF_SIZE 32
  88 
  89 #define ERR_MSG_SIZE 256
  90 
  91 #ifdef _MSC_VER
  92 # ifndef snprintf
  93 #       define snprintf  _snprintf
  94 # endif
  95 #endif
  96 
  97 typedef struct lcmsProfile_s {
  98     cmsHPROFILE pf;
  99 } lcmsProfile_t, *lcmsProfile_p;
 100 
 101 typedef union storeID_s {    /* store SProfile stuff in a Java Long */
 102     lcmsProfile_p lcmsPf;
 103     cmsHTRANSFORM xf;
 104     jobject jobj;
 105     jlong j;
 106 } storeID_t, *storeID_p;
 107 
 108 typedef union {
 109     cmsTagSignature cms;
 110     jint j;
 111 } TagSignature_t, *TagSignature_p;
 112 
 113 static jfieldID Trans_renderType_fID;
 114 static jfieldID Trans_ID_fID;
 115 static jfieldID IL_isIntPacked_fID;
 116 static jfieldID IL_dataType_fID;
 117 static jfieldID IL_pixelType_fID;
 118 static jfieldID IL_dataArray_fID;
 119 static jfieldID IL_offset_fID;
 120 static jfieldID IL_nextRowOffset_fID;
 121 static jfieldID IL_width_fID;
 122 static jfieldID IL_height_fID;
 123 static jfieldID IL_imageAtOnce_fID;
 124 
 125 JavaVM *javaVM;
 126 
 127 void errorHandler(cmsContext ContextID, cmsUInt32Number errorCode,
 128                   const char *errorText) {
 129     JNIEnv *env;
 130     char errMsg[ERR_MSG_SIZE];
 131 
 132     int count = snprintf(errMsg, ERR_MSG_SIZE,
 133                           "LCMS error %d: %s", errorCode, errorText);
 134     if (count < 0 || count >= ERR_MSG_SIZE) {
 135         count = ERR_MSG_SIZE - 1;
 136     }
 137     errMsg[count] = 0;
 138 
 139     (*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL);
 140     JNU_ThrowByName(env, "java/awt/color/CMMException", errMsg);
 141 }
 142 
 143 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
 144     javaVM = jvm;
 145 
 146     cmsSetLogErrorHandler(errorHandler);
 147     return JNI_VERSION_1_6;
 148 }
 149 
 150 void LCMS_freeProfile(JNIEnv *env, jlong ptr) {
 151     storeID_t sProfile;
 152     sProfile.j = ptr;
 153 
 154     if (sProfile.lcmsPf != NULL) {
 155         if (sProfile.lcmsPf->pf != NULL) {
 156             cmsCloseProfile(sProfile.lcmsPf->pf);
 157         }
 158         free(sProfile.lcmsPf);
 159     }
 160 }
 161 
 162 void LCMS_freeTransform(JNIEnv *env, jlong ID)
 163 {
 164     storeID_t sTrans;
 165     sTrans.j = ID;
 166     /* Passed ID is always valid native ref so there is no check for zero */
 167     cmsDeleteTransform(sTrans.xf);
 168 }
 169 
 170 /*
 171  * Class:     sun_java2d_cmm_lcms_LCMS
 172  * Method:    createNativeTransform
 173  * Signature: ([JI)J
 174  */
 175 JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
 176   (JNIEnv *env, jclass cls, jlongArray profileIDs, jint renderType,
 177    jint inFormatter, jboolean isInIntPacked,
 178    jint outFormatter, jboolean isOutIntPacked, jobject disposerRef)
 179 {
 180     cmsHPROFILE _iccArray[DF_ICC_BUF_SIZE];
 181     cmsHPROFILE *iccArray = &_iccArray[0];
 182     storeID_t sTrans;
 183     int i, j, size;
 184     jlong* ids;
 185 
 186     size = (*env)->GetArrayLength (env, profileIDs);
 187     ids = (*env)->GetLongArrayElements(env, profileIDs, 0);
 188     if (ids == NULL) {
 189         // An exception should have already been thrown.
 190         return 0L;
 191     }
 192 
 193 #ifdef _LITTLE_ENDIAN
 194     /* Reversing data packed into int for LE archs */
 195     if (isInIntPacked) {
 196         inFormatter ^= DOSWAP_SH(1);
 197     }
 198     if (isOutIntPacked) {
 199         outFormatter ^= DOSWAP_SH(1);
 200     }
 201 #endif
 202 
 203     if (DF_ICC_BUF_SIZE < size*2) {
 204         iccArray = (cmsHPROFILE*) malloc(
 205             size*2*sizeof(cmsHPROFILE));
 206         if (iccArray == NULL) {
 207             (*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0);
 208 
 209             J2dRlsTraceLn(J2D_TRACE_ERROR, "getXForm: iccArray == NULL");
 210             return 0L;
 211         }
 212     }
 213 
 214     j = 0;
 215     for (i = 0; i < size; i++) {
 216         cmsHPROFILE icc;
 217         cmsColorSpaceSignature cs;
 218 
 219         sTrans.j = ids[i];
 220         icc = sTrans.lcmsPf->pf;
 221         iccArray[j++] = icc;
 222 
 223         /* Middle non-abstract profiles should be doubled before passing to
 224          * the cmsCreateMultiprofileTransform function
 225          */
 226 
 227         cs = cmsGetColorSpace(icc);
 228         if (size > 2 && i != 0 && i != size - 1 &&
 229             cs != cmsSigXYZData && cs != cmsSigLabData)
 230         {
 231             iccArray[j++] = icc;
 232         }
 233     }
 234 
 235     sTrans.xf = cmsCreateMultiprofileTransform(iccArray, j,
 236         inFormatter, outFormatter, renderType, 0);
 237 
 238     (*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0);
 239 
 240     if (sTrans.xf == NULL) {
 241         J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_createNativeTransform: "
 242                                        "sTrans.xf == NULL");
 243         if ((*env)->ExceptionOccurred(env) == NULL) {
 244             JNU_ThrowByName(env, "java/awt/color/CMMException",
 245                             "Cannot get color transform");
 246         }
 247     } else {
 248         Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, sTrans.j);
 249     }
 250 
 251     if (iccArray != &_iccArray[0]) {
 252         free(iccArray);
 253     }
 254     return sTrans.j;
 255 }
 256 
 257 
 258 /*
 259  * Class:     sun_java2d_cmm_lcms_LCMS
 260  * Method:    loadProfile
 261  * Signature: ([B,Lsun/java2d/cmm/lcms/LCMSProfile;)V
 262  */
 263 JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative
 264   (JNIEnv *env, jobject obj, jbyteArray data, jobject disposerRef)
 265 {
 266     jbyte* dataArray;
 267     jint dataSize;
 268     storeID_t sProf;
 269     cmsHPROFILE pf;
 270 
 271     if (JNU_IsNull(env, data)) {
 272         JNU_ThrowIllegalArgumentException(env, "Invalid profile data");
 273         return 0L;
 274     }
 275 
 276     sProf.j = 0L;
 277 
 278     dataArray = (*env)->GetByteArrayElements (env, data, 0);
 279     if (dataArray == NULL) {
 280         // An exception should have already been thrown.
 281         return 0L;
 282     }
 283 
 284     dataSize = (*env)->GetArrayLength (env, data);
 285 
 286     pf = cmsOpenProfileFromMem((const void *)dataArray,
 287                                      (cmsUInt32Number) dataSize);
 288 
 289     (*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
 290 
 291     if (pf == NULL) {
 292         JNU_ThrowIllegalArgumentException(env, "Invalid profile data");
 293     } else {
 294         /* Sanity check: try to save the profile in order
 295          * to force basic validation.
 296          */
 297         cmsUInt32Number pfSize = 0;
 298         if (!cmsSaveProfileToMem(pf, NULL, &pfSize) ||
 299             pfSize < sizeof(cmsICCHeader))
 300         {
 301             JNU_ThrowIllegalArgumentException(env, "Invalid profile data");
 302 
 303             cmsCloseProfile(pf);
 304             pf = NULL;
 305         }
 306     }
 307 
 308     if (pf != NULL) {
 309         // create profile holder
 310         sProf.lcmsPf = (lcmsProfile_p)malloc(sizeof(lcmsProfile_t));
 311         if (sProf.lcmsPf != NULL) {
 312             // register the disposer record
 313             sProf.lcmsPf->pf = pf;
 314             Disposer_AddRecord(env, disposerRef, LCMS_freeProfile, sProf.j);
 315         } else {
 316             cmsCloseProfile(pf);
 317         }
 318     }
 319 
 320     return sProf.j;
 321 }
 322 
 323 /*
 324  * Class:     sun_java2d_cmm_lcms_LCMS
 325  * Method:    getProfileSizeNative
 326  * Signature: (J)I
 327  */
 328 JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileSizeNative
 329   (JNIEnv *env, jobject obj, jlong id)
 330 {
 331     storeID_t sProf;
 332     cmsUInt32Number pfSize = 0;
 333     sProf.j = id;
 334 
 335     if (cmsSaveProfileToMem(sProf.lcmsPf->pf, NULL, &pfSize) && ((jint)pfSize > 0)) {
 336         return (jint)pfSize;
 337     } else {
 338       JNU_ThrowByName(env, "java/awt/color/CMMException",
 339                       "Can not access specified profile.");
 340         return -1;
 341     }
 342 }
 343 
 344 /*
 345  * Class:     sun_java2d_cmm_lcms_LCMS
 346  * Method:    getProfileDataNative
 347  * Signature: (J[B)V
 348  */
 349 JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileDataNative
 350   (JNIEnv *env, jobject obj, jlong id, jbyteArray data)
 351 {
 352     storeID_t sProf;
 353     jint size;
 354     jbyte* dataArray;
 355     cmsUInt32Number pfSize = 0;
 356     cmsBool status;
 357 
 358     sProf.j = id;
 359 
 360     // determine actual profile size
 361     if (!cmsSaveProfileToMem(sProf.lcmsPf->pf, NULL, &pfSize)) {
 362         JNU_ThrowByName(env, "java/awt/color/CMMException",
 363                         "Can not access specified profile.");
 364         return;
 365     }
 366 
 367     // verify java buffer capacity
 368     size = (*env)->GetArrayLength(env, data);
 369     if (0 >= size || pfSize > (cmsUInt32Number)size) {
 370         JNU_ThrowByName(env, "java/awt/color/CMMException",
 371                         "Insufficient buffer capacity.");
 372         return;
 373     }
 374 
 375     dataArray = (*env)->GetByteArrayElements (env, data, 0);
 376     if (dataArray == NULL) {
 377         // An exception should have already been thrown.
 378         return;
 379     }
 380 
 381     status = cmsSaveProfileToMem(sProf.lcmsPf->pf, dataArray, &pfSize);
 382 
 383     (*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
 384 
 385     if (!status) {
 386         JNU_ThrowByName(env, "java/awt/color/CMMException",
 387                         "Can not access specified profile.");
 388         return;
 389     }
 390 }
 391 
 392 /* Get profile header info */
 393 static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize);
 394 static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize);
 395 static cmsHPROFILE _writeCookedTag(cmsHPROFILE pfTarget, cmsTagSignature sig, jbyte *pData, jint size);
 396 
 397 
 398 /*
 399  * Class:     sun_java2d_cmm_lcms_LCMS
 400  * Method:    getTagData
 401  * Signature: (JI[B)V
 402  */
 403 JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagNative
 404   (JNIEnv *env, jobject obj, jlong id, jint tagSig)
 405 {
 406     storeID_t sProf;
 407     TagSignature_t sig;
 408     cmsInt32Number tagSize;
 409 
 410     jbyte* dataArray = NULL;
 411     jbyteArray data = NULL;
 412 
 413     jint bufSize;
 414 
 415     sProf.j = id;
 416     sig.j = tagSig;
 417 
 418     if (tagSig == SigHead) {
 419         cmsBool status;
 420 
 421         // allocate java array
 422         bufSize = sizeof(cmsICCHeader);
 423         data = (*env)->NewByteArray(env, bufSize);
 424 
 425         if (data == NULL) {
 426             // An exception should have already been thrown.
 427             return NULL;
 428         }
 429 
 430         dataArray = (*env)->GetByteArrayElements (env, data, 0);
 431 
 432         if (dataArray == NULL) {
 433             // An exception should have already been thrown.
 434             return NULL;
 435         }
 436 
 437         status = _getHeaderInfo(sProf.lcmsPf->pf, dataArray, bufSize);
 438 
 439         (*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
 440 
 441         if (!status) {
 442             JNU_ThrowByName(env, "java/awt/color/CMMException",
 443                             "ICC Profile header not found");
 444             return NULL;
 445         }
 446 
 447         return data;
 448     }
 449 
 450     if (cmsIsTag(sProf.lcmsPf->pf, sig.cms)) {
 451         tagSize = cmsReadRawTag(sProf.lcmsPf->pf, sig.cms, NULL, 0);
 452     } else {
 453         JNU_ThrowByName(env, "java/awt/color/CMMException",
 454                         "ICC profile tag not found");
 455         return NULL;
 456     }
 457 
 458     // allocate java array
 459     data = (*env)->NewByteArray(env, tagSize);
 460     if (data == NULL) {
 461         // An exception should have already been thrown.
 462         return NULL;
 463     }
 464 
 465     dataArray = (*env)->GetByteArrayElements (env, data, 0);
 466 
 467     if (dataArray == NULL) {
 468         // An exception should have already been thrown.
 469         return NULL;
 470     }
 471 
 472     bufSize = cmsReadRawTag(sProf.lcmsPf->pf, sig.cms, dataArray, tagSize);
 473 
 474     (*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
 475 
 476     if (bufSize != tagSize) {
 477         JNU_ThrowByName(env, "java/awt/color/CMMException",
 478                         "Can not get tag data.");
 479         return NULL;
 480     }
 481     return data;
 482 }
 483 
 484 /*
 485  * Class:     sun_java2d_cmm_lcms_LCMS
 486  * Method:    setTagData
 487  * Signature: (JI[B)V
 488  */
 489 JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagDataNative
 490   (JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data)
 491 {
 492     storeID_t sProf;
 493     cmsHPROFILE pfReplace = NULL;
 494 
 495     TagSignature_t sig;
 496     cmsBool status = FALSE;
 497     jbyte* dataArray;
 498     int tagSize;
 499 
 500     sProf.j = id;
 501     sig.j = tagSig;
 502 
 503     if (JNU_IsNull(env, data)) {
 504         JNU_ThrowIllegalArgumentException(env, "Can not write tag data.");
 505         return;
 506     }
 507 
 508     tagSize =(*env)->GetArrayLength(env, data);
 509 
 510     dataArray = (*env)->GetByteArrayElements(env, data, 0);
 511 
 512     if (dataArray == NULL) {
 513         // An exception should have already been thrown.
 514         return;
 515     }
 516 
 517     if (tagSig == SigHead) {
 518         status  = _setHeaderInfo(sProf.lcmsPf->pf, dataArray, tagSize);
 519     } else {
 520         /*
 521         * New strategy for generic tags: create a place holder,
 522         * dump all existing tags there, dump externally supplied
 523         * tag, and return the new profile to the java.
 524         */
 525         pfReplace = _writeCookedTag(sProf.lcmsPf->pf, sig.cms, dataArray, tagSize);
 526         status = (pfReplace != NULL);
 527     }
 528 
 529     (*env)->ReleaseByteArrayElements(env, data, dataArray, 0);
 530 
 531     if (!status) {
 532         JNU_ThrowIllegalArgumentException(env, "Can not write tag data.");
 533     } else if (pfReplace != NULL) {
 534         cmsCloseProfile(sProf.lcmsPf->pf);
 535         sProf.lcmsPf->pf = pfReplace;
 536     }
 537 }
 538 
 539 void* getILData (JNIEnv *env, jobject img, jint* pDataType,
 540                  jobject* pDataObject) {
 541     void* result = NULL;
 542     *pDataType = (*env)->GetIntField (env, img, IL_dataType_fID);
 543     *pDataObject = (*env)->GetObjectField(env, img, IL_dataArray_fID);
 544     switch (*pDataType) {
 545         case DT_BYTE:
 546             result = (*env)->GetByteArrayElements (env, *pDataObject, 0);
 547             break;
 548         case DT_SHORT:
 549             result = (*env)->GetShortArrayElements (env, *pDataObject, 0);
 550             break;
 551         case DT_INT:
 552             result = (*env)->GetIntArrayElements (env, *pDataObject, 0);
 553             break;
 554         case DT_DOUBLE:
 555             result = (*env)->GetDoubleArrayElements (env, *pDataObject, 0);
 556             break;
 557     }
 558 
 559     return result;
 560 }
 561 
 562 void releaseILData (JNIEnv *env, void* pData, jint dataType,
 563                     jobject dataObject) {
 564     switch (dataType) {
 565         case DT_BYTE:
 566             (*env)->ReleaseByteArrayElements(env,dataObject,(jbyte*)pData,0);
 567             break;
 568         case DT_SHORT:
 569             (*env)->ReleaseShortArrayElements(env,dataObject,(jshort*)pData, 0);
 570             break;
 571         case DT_INT:
 572             (*env)->ReleaseIntArrayElements(env,dataObject,(jint*)pData,0);
 573             break;
 574         case DT_DOUBLE:
 575             (*env)->ReleaseDoubleArrayElements(env,dataObject,(jdouble*)pData,
 576                                                0);
 577             break;
 578     }
 579 }
 580 
 581 /*
 582  * Class:     sun_java2d_cmm_lcms_LCMS
 583  * Method:    colorConvert
 584  * Signature: (Lsun/java2d/cmm/lcms/LCMSTransform;Lsun/java2d/cmm/lcms/LCMSImageLayout;Lsun/java2d/cmm/lcms/LCMSImageLayout;)V
 585  */
 586 JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert
 587   (JNIEnv *env, jclass obj, jobject trans, jobject src, jobject dst)
 588 {
 589     storeID_t sTrans;
 590     int srcDType, dstDType;
 591     int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset;
 592     int width, height, i;
 593     void* inputBuffer;
 594     void* outputBuffer;
 595     char* inputRow;
 596     char* outputRow;
 597     jobject srcData, dstData;
 598     jboolean srcAtOnce = JNI_FALSE, dstAtOnce = JNI_FALSE;
 599 
 600     srcOffset = (*env)->GetIntField (env, src, IL_offset_fID);
 601     srcNextRowOffset = (*env)->GetIntField (env, src, IL_nextRowOffset_fID);
 602     dstOffset = (*env)->GetIntField (env, dst, IL_offset_fID);
 603     dstNextRowOffset = (*env)->GetIntField (env, dst, IL_nextRowOffset_fID);
 604     width = (*env)->GetIntField (env, src, IL_width_fID);
 605     height = (*env)->GetIntField (env, src, IL_height_fID);
 606 
 607     srcAtOnce = (*env)->GetBooleanField(env, src, IL_imageAtOnce_fID);
 608     dstAtOnce = (*env)->GetBooleanField(env, dst, IL_imageAtOnce_fID);
 609 
 610     sTrans.j = (*env)->GetLongField (env, trans, Trans_ID_fID);
 611 
 612     if (sTrans.xf == NULL) {
 613         J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL");
 614         JNU_ThrowByName(env, "java/awt/color/CMMException",
 615                         "Cannot get color transform");
 616         return;
 617     }
 618 
 619 
 620     inputBuffer = getILData (env, src, &srcDType, &srcData);
 621 
 622     if (inputBuffer == NULL) {
 623         J2dRlsTraceLn(J2D_TRACE_ERROR, "");
 624         // An exception should have already been thrown.
 625         return;
 626     }
 627 
 628     outputBuffer = getILData (env, dst, &dstDType, &dstData);
 629 
 630     if (outputBuffer == NULL) {
 631         releaseILData(env, inputBuffer, srcDType, srcData);
 632         // An exception should have already been thrown.
 633         return;
 634     }
 635 
 636     inputRow = (char*)inputBuffer + srcOffset;
 637     outputRow = (char*)outputBuffer + dstOffset;
 638 
 639     if (srcAtOnce && dstAtOnce) {
 640         cmsDoTransform(sTrans.xf, inputRow, outputRow, width * height);
 641     } else {
 642         for (i = 0; i < height; i++) {
 643             cmsDoTransform(sTrans.xf, inputRow, outputRow, width);
 644             inputRow += srcNextRowOffset;
 645             outputRow += dstNextRowOffset;
 646         }
 647     }
 648 
 649     releaseILData(env, inputBuffer, srcDType, srcData);
 650     releaseILData(env, outputBuffer, dstDType, dstData);
 651 }
 652 
 653 /*
 654  * Class:     sun_java2d_cmm_lcms_LCMS
 655  * Method:    getProfileID
 656  * Signature: (Ljava/awt/color/ICC_Profile;)Lsun/java2d/cmm/lcms/LCMSProfile
 657  */
 658 JNIEXPORT jobject JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileID
 659   (JNIEnv *env, jclass cls, jobject pf)
 660 {
 661     jclass clsLcmsProfile;
 662     jobject cmmProfile;
 663     jfieldID fid = (*env)->GetFieldID (env,
 664         (*env)->GetObjectClass(env, pf),
 665         "cmmProfile", "Lsun/java2d/cmm/Profile;");
 666     if (fid == NULL) {
 667         return NULL;
 668     }
 669 
 670     clsLcmsProfile = (*env)->FindClass(env,
 671             "sun/java2d/cmm/lcms/LCMSProfile");
 672     if (clsLcmsProfile == NULL) {
 673         return NULL;
 674     }
 675 
 676     cmmProfile = (*env)->GetObjectField (env, pf, fid);
 677 
 678     if (JNU_IsNull(env, cmmProfile)) {
 679         return NULL;
 680     }
 681     if ((*env)->IsInstanceOf(env, cmmProfile, clsLcmsProfile)) {
 682         return cmmProfile;
 683     }
 684     return NULL;
 685 }
 686 
 687 /*
 688  * Class:     sun_java2d_cmm_lcms_LCMS
 689  * Method:    initLCMS
 690  * Signature: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)V
 691  */
 692 JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_initLCMS
 693   (JNIEnv *env, jclass cls, jclass Trans, jclass IL, jclass Pf)
 694 {
 695     /* TODO: move initialization of the IDs to the static blocks of
 696      * corresponding classes to avoid problems with invalidating ids by class
 697      * unloading
 698      */
 699     Trans_renderType_fID = (*env)->GetFieldID (env, Trans, "renderType", "I");
 700     if (Trans_renderType_fID == NULL) {
 701         return;
 702     }
 703     Trans_ID_fID = (*env)->GetFieldID (env, Trans, "ID", "J");
 704     if (Trans_ID_fID == NULL) {
 705         return;
 706     }
 707 
 708     IL_isIntPacked_fID = (*env)->GetFieldID (env, IL, "isIntPacked", "Z");
 709     if (IL_isIntPacked_fID == NULL) {
 710         return;
 711     }
 712     IL_dataType_fID = (*env)->GetFieldID (env, IL, "dataType", "I");
 713     if (IL_dataType_fID == NULL) {
 714         return;
 715     }
 716     IL_pixelType_fID = (*env)->GetFieldID (env, IL, "pixelType", "I");
 717     if (IL_pixelType_fID == NULL) {
 718         return;
 719     }
 720     IL_dataArray_fID = (*env)->GetFieldID(env, IL, "dataArray",
 721                                           "Ljava/lang/Object;");
 722     if (IL_dataArray_fID == NULL) {
 723         return;
 724     }
 725     IL_width_fID = (*env)->GetFieldID (env, IL, "width", "I");
 726     if (IL_width_fID == NULL) {
 727         return;
 728     }
 729     IL_height_fID = (*env)->GetFieldID (env, IL, "height", "I");
 730     if (IL_height_fID == NULL) {
 731         return;
 732     }
 733     IL_offset_fID = (*env)->GetFieldID (env, IL, "offset", "I");
 734     if (IL_offset_fID == NULL) {
 735         return;
 736     }
 737     IL_imageAtOnce_fID = (*env)->GetFieldID (env, IL, "imageAtOnce", "Z");
 738     if (IL_imageAtOnce_fID == NULL) {
 739         return;
 740     }
 741     IL_nextRowOffset_fID = (*env)->GetFieldID (env, IL, "nextRowOffset", "I");
 742     if (IL_nextRowOffset_fID == NULL) {
 743         return;
 744     }
 745 }
 746 
 747 static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize)
 748 {
 749   cmsUInt32Number pfSize = 0;
 750   cmsUInt8Number* pfBuffer = NULL;
 751   cmsBool status = FALSE;
 752 
 753   if (!cmsSaveProfileToMem(pf, NULL, &pfSize) ||
 754       pfSize < sizeof(cmsICCHeader) ||
 755       bufferSize < sizeof(cmsICCHeader))
 756   {
 757     return FALSE;
 758   }
 759 
 760   pfBuffer = malloc(pfSize);
 761   if (pfBuffer == NULL) {
 762     return FALSE;
 763   }
 764 
 765   // load raw profile data into the buffer
 766   if (cmsSaveProfileToMem(pf, pfBuffer, &pfSize)) {
 767     memcpy(pBuffer, pfBuffer, sizeof(cmsICCHeader));
 768     status = TRUE;
 769   }
 770   free(pfBuffer);
 771   return status;
 772 }
 773 
 774 static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize)
 775 {
 776   cmsICCHeader pfHeader = { 0 };
 777 
 778   if (pBuffer == NULL || bufferSize < sizeof(cmsICCHeader)) {
 779     return FALSE;
 780   }
 781 
 782   memcpy(&pfHeader, pBuffer, sizeof(cmsICCHeader));
 783 
 784   // now set header fields, which we can access using the lcms2 public API
 785   cmsSetHeaderFlags(pf, pfHeader.flags);
 786   cmsSetHeaderManufacturer(pf, pfHeader.manufacturer);
 787   cmsSetHeaderModel(pf, pfHeader.model);
 788   cmsSetHeaderAttributes(pf, pfHeader.attributes);
 789   cmsSetHeaderProfileID(pf, (cmsUInt8Number*)&(pfHeader.profileID));
 790   cmsSetHeaderRenderingIntent(pf, pfHeader.renderingIntent);
 791   cmsSetPCS(pf, pfHeader.pcs);
 792   cmsSetColorSpace(pf, pfHeader.colorSpace);
 793   cmsSetDeviceClass(pf, pfHeader.deviceClass);
 794   cmsSetEncodedICCversion(pf, pfHeader.version);
 795 
 796   return TRUE;
 797 }
 798 
 799 /* Returns new profile handler, if it was created successfully,
 800    NULL otherwise.
 801    */
 802 static cmsHPROFILE _writeCookedTag(const cmsHPROFILE pfTarget,
 803                                const cmsTagSignature sig,
 804                                jbyte *pData, jint size)
 805 {
 806     cmsUInt32Number pfSize = 0;
 807     const cmsInt32Number tagCount = cmsGetTagCount(pfTarget);
 808     cmsInt32Number i;
 809     cmsHPROFILE pfSanity = NULL;
 810 
 811     cmsICCHeader hdr = { 0 };
 812 
 813     cmsHPROFILE p = cmsCreateProfilePlaceholder(NULL);
 814 
 815     if (NULL == p) {
 816         return NULL;
 817     }
 818 
 819     // Populate the placeholder's header according to target profile
 820     hdr.flags = cmsGetHeaderFlags(pfTarget);
 821     hdr.renderingIntent = cmsGetHeaderRenderingIntent(pfTarget);
 822     hdr.manufacturer = cmsGetHeaderManufacturer(pfTarget);
 823     hdr.model = cmsGetHeaderModel(pfTarget);
 824     hdr.pcs = cmsGetPCS(pfTarget);
 825     hdr.colorSpace = cmsGetColorSpace(pfTarget);
 826     hdr.deviceClass = cmsGetDeviceClass(pfTarget);
 827     hdr.version = cmsGetEncodedICCversion(pfTarget);
 828     cmsGetHeaderAttributes(pfTarget, &hdr.attributes);
 829     cmsGetHeaderProfileID(pfTarget, (cmsUInt8Number*)&hdr.profileID);
 830 
 831     cmsSetHeaderFlags(p, hdr.flags);
 832     cmsSetHeaderManufacturer(p, hdr.manufacturer);
 833     cmsSetHeaderModel(p, hdr.model);
 834     cmsSetHeaderAttributes(p, hdr.attributes);
 835     cmsSetHeaderProfileID(p, (cmsUInt8Number*)&(hdr.profileID));
 836     cmsSetHeaderRenderingIntent(p, hdr.renderingIntent);
 837     cmsSetPCS(p, hdr.pcs);
 838     cmsSetColorSpace(p, hdr.colorSpace);
 839     cmsSetDeviceClass(p, hdr.deviceClass);
 840     cmsSetEncodedICCversion(p, hdr.version);
 841 
 842     // now write the user supplied tag
 843     if (size <= 0 || !cmsWriteRawTag(p, sig, pData, size)) {
 844         cmsCloseProfile(p);
 845         return NULL;
 846     }
 847 
 848     // copy tags from the original profile
 849     for (i = 0; i < tagCount; i++) {
 850         cmsBool isTagReady = FALSE;
 851         const cmsTagSignature s = cmsGetTagSignature(pfTarget, i);
 852         const cmsInt32Number tagSize = cmsReadRawTag(pfTarget, s, NULL, 0);
 853 
 854         if (s == sig) {
 855             // skip the user supplied tag
 856             continue;
 857         }
 858 
 859         // read raw tag from the original profile
 860         if (tagSize > 0) {
 861             cmsUInt8Number* buf = (cmsUInt8Number*)malloc(tagSize);
 862             if (buf != NULL) {
 863                 if (tagSize ==  cmsReadRawTag(pfTarget, s, buf, tagSize)) {
 864                     // now we are ready to write the tag
 865                     isTagReady = cmsWriteRawTag(p, s, buf, tagSize);
 866                 }
 867                 free(buf);
 868             }
 869         }
 870 
 871         if (!isTagReady) {
 872             cmsCloseProfile(p);
 873             return NULL;
 874         }
 875     }
 876 
 877     // now we have all tags moved to the new profile.
 878     // do some sanity checks: write it to a memory buffer and read again.
 879     if (cmsSaveProfileToMem(p, NULL, &pfSize)) {
 880         void* buf = malloc(pfSize);
 881         if (buf != NULL) {
 882             // load raw profile data into the buffer
 883             if (cmsSaveProfileToMem(p, buf, &pfSize)) {
 884                 pfSanity = cmsOpenProfileFromMem(buf, pfSize);
 885             }
 886             free(buf);
 887         }
 888     }
 889 
 890     if (pfSanity == NULL) {
 891         // for some reason, we failed to save and read the updated profile
 892         // It likely indicates that the profile is not correct, so we report
 893         // a failure here.
 894         cmsCloseProfile(p);
 895         p =  NULL;
 896     } else {
 897         // do final check whether we can read and handle the the target tag.
 898         const void* pTag = cmsReadTag(pfSanity, sig);
 899         if (pTag == NULL) {
 900             // the tag can not be cooked
 901             cmsCloseProfile(p);
 902             p = NULL;
 903         }
 904         cmsCloseProfile(pfSanity);
 905         pfSanity = NULL;
 906     }
 907 
 908     return p;
 909 }