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