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 189 #ifdef _LITTLE_ENDIAN 190 /* Reversing data packed into int for LE archs */ 191 if (isInIntPacked) { 192 inFormatter ^= DOSWAP_SH(1); 193 } 194 if (isOutIntPacked) { 195 outFormatter ^= DOSWAP_SH(1); 196 } 197 #endif 198 199 if (DF_ICC_BUF_SIZE < size*2) { 200 iccArray = (cmsHPROFILE*) malloc( 201 size*2*sizeof(cmsHPROFILE)); 202 if (iccArray == NULL) { 203 (*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0); 204 205 J2dRlsTraceLn(J2D_TRACE_ERROR, "getXForm: iccArray == NULL"); 206 return 0L; 207 } 208 } 209 210 j = 0; 211 for (i = 0; i < size; i++) { 212 cmsHPROFILE icc; 213 cmsColorSpaceSignature cs; 214 215 sTrans.j = ids[i]; 216 icc = sTrans.lcmsPf->pf; 217 iccArray[j++] = icc; 218 219 /* Middle non-abstract profiles should be doubled before passing to 220 * the cmsCreateMultiprofileTransform function 221 */ 222 223 cs = cmsGetColorSpace(icc); 224 if (size > 2 && i != 0 && i != size - 1 && 225 cs != cmsSigXYZData && cs != cmsSigLabData) 226 { 227 iccArray[j++] = icc; 228 } 229 } 230 231 sTrans.xf = cmsCreateMultiprofileTransform(iccArray, j, 232 inFormatter, outFormatter, renderType, 0); 233 234 (*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0); 235 236 if (sTrans.xf == NULL) { 237 J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_createNativeTransform: " 238 "sTrans.xf == NULL"); 239 if ((*env)->ExceptionOccurred(env) == NULL) { 240 JNU_ThrowByName(env, "java/awt/color/CMMException", 241 "Cannot get color transform"); 242 } 243 } else { 244 Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, sTrans.j); 245 } 246 247 if (iccArray != &_iccArray[0]) { 248 free(iccArray); 249 } 250 return sTrans.j; 251 } 252 253 254 /* 255 * Class: sun_java2d_cmm_lcms_LCMS 256 * Method: loadProfile 257 * Signature: ([B,Lsun/java2d/cmm/lcms/LCMSProfile;)V 258 */ 259 JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative 260 (JNIEnv *env, jobject obj, jbyteArray data, jobject disposerRef) 261 { 262 jbyte* dataArray; 263 jint dataSize; 264 storeID_t sProf; 265 cmsHPROFILE pf; 266 267 if (JNU_IsNull(env, data)) { 268 JNU_ThrowIllegalArgumentException(env, "Invalid profile data"); 269 return 0L; 270 } 271 272 sProf.j = 0L; 273 274 dataArray = (*env)->GetByteArrayElements (env, data, 0); 275 dataSize = (*env)->GetArrayLength (env, data); 276 277 if (dataArray == NULL) { 278 JNU_ThrowIllegalArgumentException(env, "Invalid profile data"); 279 return 0L; 280 } 281 282 pf = cmsOpenProfileFromMem((const void *)dataArray, 283 (cmsUInt32Number) dataSize); 284 285 (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); 286 287 if (pf == NULL) { 288 JNU_ThrowIllegalArgumentException(env, "Invalid profile data"); 289 } else { 290 /* Sanity check: try to save the profile in order 291 * to force basic validation. 292 */ 293 cmsUInt32Number pfSize = 0; 294 if (!cmsSaveProfileToMem(pf, NULL, &pfSize) || 295 pfSize < sizeof(cmsICCHeader)) 296 { 297 JNU_ThrowIllegalArgumentException(env, "Invalid profile data"); 298 299 cmsCloseProfile(pf); 300 pf = NULL; 301 } 302 } 303 304 if (pf != NULL) { 305 // create profile holder 306 sProf.lcmsPf = (lcmsProfile_p)malloc(sizeof(lcmsProfile_t)); 307 if (sProf.lcmsPf != NULL) { 308 // register the disposer record 309 sProf.lcmsPf->pf = pf; 310 Disposer_AddRecord(env, disposerRef, LCMS_freeProfile, sProf.j); 311 } else { 312 cmsCloseProfile(pf); 313 } 314 } 315 316 return sProf.j; 317 } 318 319 /* 320 * Class: sun_java2d_cmm_lcms_LCMS 321 * Method: getProfileSizeNative 322 * Signature: (J)I 323 */ 324 JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileSizeNative 325 (JNIEnv *env, jobject obj, jlong id) 326 { 327 storeID_t sProf; 328 cmsUInt32Number pfSize = 0; 329 sProf.j = id; 330 331 if (cmsSaveProfileToMem(sProf.lcmsPf->pf, NULL, &pfSize) && ((jint)pfSize > 0)) { 332 return (jint)pfSize; 333 } else { 334 JNU_ThrowByName(env, "java/awt/color/CMMException", 335 "Can not access specified profile."); 336 return -1; 337 } 338 } 339 340 /* 341 * Class: sun_java2d_cmm_lcms_LCMS 342 * Method: getProfileDataNative 343 * Signature: (J[B)V 344 */ 345 JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileDataNative 346 (JNIEnv *env, jobject obj, jlong id, jbyteArray data) 347 { 348 storeID_t sProf; 349 jint size; 350 jbyte* dataArray; 351 cmsUInt32Number pfSize = 0; 352 cmsBool status; 353 354 sProf.j = id; 355 356 // determine actual profile size 357 if (!cmsSaveProfileToMem(sProf.lcmsPf->pf, NULL, &pfSize)) { 358 JNU_ThrowByName(env, "java/awt/color/CMMException", 359 "Can not access specified profile."); 360 return; 361 } 362 363 // verify java buffer capacity 364 size = (*env)->GetArrayLength(env, data); 365 if (0 >= size || pfSize > (cmsUInt32Number)size) { 366 JNU_ThrowByName(env, "java/awt/color/CMMException", 367 "Insufficient buffer capacity."); 368 return; 369 } 370 371 dataArray = (*env)->GetByteArrayElements (env, data, 0); 372 373 status = cmsSaveProfileToMem(sProf.lcmsPf->pf, dataArray, &pfSize); 374 375 (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); 376 377 if (!status) { 378 JNU_ThrowByName(env, "java/awt/color/CMMException", 379 "Can not access specified profile."); 380 return; 381 } 382 } 383 384 /* Get profile header info */ 385 static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize); 386 static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize); 387 static cmsHPROFILE _writeCookedTag(cmsHPROFILE pfTarget, cmsTagSignature sig, jbyte *pData, jint size); 388 389 390 /* 391 * Class: sun_java2d_cmm_lcms_LCMS 392 * Method: getTagData 393 * Signature: (JI[B)V 394 */ 395 JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagNative 396 (JNIEnv *env, jobject obj, jlong id, jint tagSig) 397 { 398 storeID_t sProf; 399 TagSignature_t sig; 400 cmsInt32Number tagSize; 401 402 jbyte* dataArray = NULL; 403 jbyteArray data = NULL; 404 405 jint bufSize; 406 407 sProf.j = id; 408 sig.j = tagSig; 409 410 if (tagSig == SigHead) { 411 cmsBool status; 412 413 // allocate java array 414 bufSize = sizeof(cmsICCHeader); 415 data = (*env)->NewByteArray(env, bufSize); 416 417 if (data == NULL) { 418 JNU_ThrowByName(env, "java/awt/color/CMMException", 419 "Unable to allocate buffer"); 420 return NULL; 421 } 422 423 dataArray = (*env)->GetByteArrayElements (env, data, 0); 424 425 if (dataArray == NULL) { 426 JNU_ThrowByName(env, "java/awt/color/CMMException", 427 "Unable to get buffer"); 428 return NULL; 429 } 430 431 status = _getHeaderInfo(sProf.lcmsPf->pf, dataArray, bufSize); 432 433 (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); 434 435 if (!status) { 436 JNU_ThrowByName(env, "java/awt/color/CMMException", 437 "ICC Profile header not found"); 438 return NULL; 439 } 440 441 return data; 442 } 443 444 if (cmsIsTag(sProf.lcmsPf->pf, sig.cms)) { 445 tagSize = cmsReadRawTag(sProf.lcmsPf->pf, sig.cms, NULL, 0); 446 } else { 447 JNU_ThrowByName(env, "java/awt/color/CMMException", 448 "ICC profile tag not found"); 449 return NULL; 450 } 451 452 // allocate java array 453 data = (*env)->NewByteArray(env, tagSize); 454 if (data == NULL) { 455 JNU_ThrowByName(env, "java/awt/color/CMMException", 456 "Unable to allocate buffer"); 457 return NULL; 458 } 459 460 dataArray = (*env)->GetByteArrayElements (env, data, 0); 461 462 if (dataArray == NULL) { 463 JNU_ThrowByName(env, "java/awt/color/CMMException", 464 "Unable to get buffer"); 465 return NULL; 466 } 467 468 bufSize = cmsReadRawTag(sProf.lcmsPf->pf, sig.cms, dataArray, tagSize); 469 470 (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); 471 472 if (bufSize != tagSize) { 473 JNU_ThrowByName(env, "java/awt/color/CMMException", 474 "Can not get tag data."); 475 return NULL; 476 } 477 return data; 478 } 479 480 /* 481 * Class: sun_java2d_cmm_lcms_LCMS 482 * Method: setTagData 483 * Signature: (JI[B)V 484 */ 485 JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagDataNative 486 (JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data) 487 { 488 storeID_t sProf; 489 cmsHPROFILE pfReplace = NULL; 490 491 TagSignature_t sig; 492 cmsBool status = FALSE; 493 jbyte* dataArray; 494 int tagSize; 495 496 sProf.j = id; 497 sig.j = tagSig; 498 499 if (JNU_IsNull(env, data)) { 500 JNU_ThrowIllegalArgumentException(env, "Can not write tag data."); 501 return; 502 } 503 504 tagSize =(*env)->GetArrayLength(env, data); 505 506 dataArray = (*env)->GetByteArrayElements(env, data, 0); 507 508 if (dataArray == NULL) { 509 JNU_ThrowIllegalArgumentException(env, "Can not write tag data."); 510 return; 511 } 512 513 if (tagSig == SigHead) { 514 status = _setHeaderInfo(sProf.lcmsPf->pf, dataArray, tagSize); 515 } else { 516 /* 517 * New strategy for generic tags: create a place holder, 518 * dump all existing tags there, dump externally supplied 519 * tag, and return the new profile to the java. 520 */ 521 pfReplace = _writeCookedTag(sProf.lcmsPf->pf, sig.cms, dataArray, tagSize); 522 status = (pfReplace != NULL); 523 } 524 525 (*env)->ReleaseByteArrayElements(env, data, dataArray, 0); 526 527 if (!status) { 528 JNU_ThrowIllegalArgumentException(env, "Can not write tag data."); 529 } else if (pfReplace != NULL) { 530 cmsCloseProfile(sProf.lcmsPf->pf); 531 sProf.lcmsPf->pf = pfReplace; 532 } 533 } 534 535 void* getILData (JNIEnv *env, jobject img, jint* pDataType, 536 jobject* pDataObject) { 537 void* result = NULL; 538 *pDataType = (*env)->GetIntField (env, img, IL_dataType_fID); 539 *pDataObject = (*env)->GetObjectField(env, img, IL_dataArray_fID); 540 switch (*pDataType) { 541 case DT_BYTE: 542 result = (*env)->GetByteArrayElements (env, *pDataObject, 0); 543 break; 544 case DT_SHORT: 545 result = (*env)->GetShortArrayElements (env, *pDataObject, 0); 546 break; 547 case DT_INT: 548 result = (*env)->GetIntArrayElements (env, *pDataObject, 0); 549 break; 550 case DT_DOUBLE: 551 result = (*env)->GetDoubleArrayElements (env, *pDataObject, 0); 552 break; 553 } 554 555 return result; 556 } 557 558 void releaseILData (JNIEnv *env, void* pData, jint dataType, 559 jobject dataObject) { 560 switch (dataType) { 561 case DT_BYTE: 562 (*env)->ReleaseByteArrayElements(env,dataObject,(jbyte*)pData,0); 563 break; 564 case DT_SHORT: 565 (*env)->ReleaseShortArrayElements(env,dataObject,(jshort*)pData, 0); 566 break; 567 case DT_INT: 568 (*env)->ReleaseIntArrayElements(env,dataObject,(jint*)pData,0); 569 break; 570 case DT_DOUBLE: 571 (*env)->ReleaseDoubleArrayElements(env,dataObject,(jdouble*)pData, 572 0); 573 break; 574 } 575 } 576 577 /* 578 * Class: sun_java2d_cmm_lcms_LCMS 579 * Method: colorConvert 580 * Signature: (Lsun/java2d/cmm/lcms/LCMSTransform;Lsun/java2d/cmm/lcms/LCMSImageLayout;Lsun/java2d/cmm/lcms/LCMSImageLayout;)V 581 */ 582 JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert 583 (JNIEnv *env, jclass obj, jobject trans, jobject src, jobject dst) 584 { 585 storeID_t sTrans; 586 int srcDType, dstDType; 587 int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset; 588 int width, height, i; 589 void* inputBuffer; 590 void* outputBuffer; 591 char* inputRow; 592 char* outputRow; 593 jobject srcData, dstData; 594 jboolean srcAtOnce = JNI_FALSE, dstAtOnce = JNI_FALSE; 595 596 srcOffset = (*env)->GetIntField (env, src, IL_offset_fID); 597 srcNextRowOffset = (*env)->GetIntField (env, src, IL_nextRowOffset_fID); 598 dstOffset = (*env)->GetIntField (env, dst, IL_offset_fID); 599 dstNextRowOffset = (*env)->GetIntField (env, dst, IL_nextRowOffset_fID); 600 width = (*env)->GetIntField (env, src, IL_width_fID); 601 height = (*env)->GetIntField (env, src, IL_height_fID); 602 603 srcAtOnce = (*env)->GetBooleanField(env, src, IL_imageAtOnce_fID); 604 dstAtOnce = (*env)->GetBooleanField(env, dst, IL_imageAtOnce_fID); 605 606 sTrans.j = (*env)->GetLongField (env, trans, Trans_ID_fID); 607 608 if (sTrans.xf == NULL) { 609 J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL"); 610 JNU_ThrowByName(env, "java/awt/color/CMMException", 611 "Cannot get color transform"); 612 return; 613 } 614 615 616 inputBuffer = getILData (env, src, &srcDType, &srcData); 617 618 if (inputBuffer == NULL) { 619 J2dRlsTraceLn(J2D_TRACE_ERROR, ""); 620 JNU_ThrowByName(env, "java/awt/color/CMMException", 621 "Cannot get input data"); 622 return; 623 } 624 625 outputBuffer = getILData (env, dst, &dstDType, &dstData); 626 627 if (outputBuffer == NULL) { 628 releaseILData(env, inputBuffer, srcDType, srcData); 629 JNU_ThrowByName(env, "java/awt/color/CMMException", 630 "Cannot get output data"); 631 return; 632 } 633 634 inputRow = (char*)inputBuffer + srcOffset; 635 outputRow = (char*)outputBuffer + dstOffset; 636 637 if (srcAtOnce && dstAtOnce) { 638 cmsDoTransform(sTrans.xf, inputRow, outputRow, width * height); 639 } else { 640 for (i = 0; i < height; i++) { 641 cmsDoTransform(sTrans.xf, inputRow, outputRow, width); 642 inputRow += srcNextRowOffset; 643 outputRow += dstNextRowOffset; 644 } 645 } 646 647 releaseILData(env, inputBuffer, srcDType, srcData); 648 releaseILData(env, outputBuffer, dstDType, dstData); 649 } 650 651 /* 652 * Class: sun_java2d_cmm_lcms_LCMS 653 * Method: getProfileID 654 * Signature: (Ljava/awt/color/ICC_Profile;)Lsun/java2d/cmm/lcms/LCMSProfile 655 */ 656 JNIEXPORT jobject JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileID 657 (JNIEnv *env, jclass cls, jobject pf) 658 { 659 jfieldID fid = (*env)->GetFieldID (env, 660 (*env)->GetObjectClass(env, pf), 661 "cmmProfile", "Lsun/java2d/cmm/Profile;"); 662 663 jclass clsLcmsProfile = (*env)->FindClass(env, 664 "sun/java2d/cmm/lcms/LCMSProfile"); 665 666 jobject cmmProfile = (*env)->GetObjectField (env, pf, fid); 667 668 if (JNU_IsNull(env, cmmProfile)) { 669 return NULL; 670 } 671 if ((*env)->IsInstanceOf(env, cmmProfile, clsLcmsProfile)) { 672 return cmmProfile; 673 } 674 return NULL; 675 } 676 677 /* 678 * Class: sun_java2d_cmm_lcms_LCMS 679 * Method: initLCMS 680 * Signature: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)V 681 */ 682 JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_initLCMS 683 (JNIEnv *env, jclass cls, jclass Trans, jclass IL, jclass Pf) 684 { 685 /* TODO: move initialization of the IDs to the static blocks of 686 * corresponding classes to avoid problems with invalidating ids by class 687 * unloading 688 */ 689 Trans_renderType_fID = (*env)->GetFieldID (env, Trans, "renderType", "I"); 690 Trans_ID_fID = (*env)->GetFieldID (env, Trans, "ID", "J"); 691 692 IL_isIntPacked_fID = (*env)->GetFieldID (env, IL, "isIntPacked", "Z"); 693 IL_dataType_fID = (*env)->GetFieldID (env, IL, "dataType", "I"); 694 IL_pixelType_fID = (*env)->GetFieldID (env, IL, "pixelType", "I"); 695 IL_dataArray_fID = (*env)->GetFieldID(env, IL, "dataArray", 696 "Ljava/lang/Object;"); 697 IL_width_fID = (*env)->GetFieldID (env, IL, "width", "I"); 698 IL_height_fID = (*env)->GetFieldID (env, IL, "height", "I"); 699 IL_offset_fID = (*env)->GetFieldID (env, IL, "offset", "I"); 700 IL_imageAtOnce_fID = (*env)->GetFieldID (env, IL, "imageAtOnce", "Z"); 701 IL_nextRowOffset_fID = (*env)->GetFieldID (env, IL, "nextRowOffset", "I"); 702 } 703 704 static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize) 705 { 706 cmsUInt32Number pfSize = 0; 707 cmsUInt8Number* pfBuffer = NULL; 708 cmsBool status = FALSE; 709 710 if (!cmsSaveProfileToMem(pf, NULL, &pfSize) || 711 pfSize < sizeof(cmsICCHeader) || 712 bufferSize < sizeof(cmsICCHeader)) 713 { 714 return FALSE; 715 } 716 717 pfBuffer = malloc(pfSize); 718 if (pfBuffer == NULL) { 719 return FALSE; 720 } 721 722 // load raw profile data into the buffer 723 if (cmsSaveProfileToMem(pf, pfBuffer, &pfSize)) { 724 memcpy(pBuffer, pfBuffer, sizeof(cmsICCHeader)); 725 status = TRUE; 726 } 727 free(pfBuffer); 728 return status; 729 } 730 731 static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize) 732 { 733 cmsICCHeader pfHeader = { 0 }; 734 735 if (pBuffer == NULL || bufferSize < sizeof(cmsICCHeader)) { 736 return FALSE; 737 } 738 739 memcpy(&pfHeader, pBuffer, sizeof(cmsICCHeader)); 740 741 // now set header fields, which we can access using the lcms2 public API 742 cmsSetHeaderFlags(pf, pfHeader.flags); 743 cmsSetHeaderManufacturer(pf, pfHeader.manufacturer); 744 cmsSetHeaderModel(pf, pfHeader.model); 745 cmsSetHeaderAttributes(pf, pfHeader.attributes); 746 cmsSetHeaderProfileID(pf, (cmsUInt8Number*)&(pfHeader.profileID)); 747 cmsSetHeaderRenderingIntent(pf, pfHeader.renderingIntent); 748 cmsSetPCS(pf, pfHeader.pcs); 749 cmsSetColorSpace(pf, pfHeader.colorSpace); 750 cmsSetDeviceClass(pf, pfHeader.deviceClass); 751 cmsSetEncodedICCversion(pf, pfHeader.version); 752 753 return TRUE; 754 } 755 756 /* Returns new profile handler, if it was created successfully, 757 NULL otherwise. 758 */ 759 static cmsHPROFILE _writeCookedTag(const cmsHPROFILE pfTarget, 760 const cmsTagSignature sig, 761 jbyte *pData, jint size) 762 { 763 cmsUInt32Number pfSize = 0; 764 const cmsInt32Number tagCount = cmsGetTagCount(pfTarget); 765 cmsInt32Number i; 766 cmsHPROFILE pfSanity = NULL; 767 768 cmsICCHeader hdr = { 0 }; 769 770 cmsHPROFILE p = cmsCreateProfilePlaceholder(NULL); 771 772 if (NULL == p) { 773 return NULL; 774 } 775 776 // Populate the placeholder's header according to target profile 777 hdr.flags = cmsGetHeaderFlags(pfTarget); 778 hdr.renderingIntent = cmsGetHeaderRenderingIntent(pfTarget); 779 hdr.manufacturer = cmsGetHeaderManufacturer(pfTarget); 780 hdr.model = cmsGetHeaderModel(pfTarget); 781 hdr.pcs = cmsGetPCS(pfTarget); 782 hdr.colorSpace = cmsGetColorSpace(pfTarget); 783 hdr.deviceClass = cmsGetDeviceClass(pfTarget); 784 hdr.version = cmsGetEncodedICCversion(pfTarget); 785 cmsGetHeaderAttributes(pfTarget, &hdr.attributes); 786 cmsGetHeaderProfileID(pfTarget, (cmsUInt8Number*)&hdr.profileID); 787 788 cmsSetHeaderFlags(p, hdr.flags); 789 cmsSetHeaderManufacturer(p, hdr.manufacturer); 790 cmsSetHeaderModel(p, hdr.model); 791 cmsSetHeaderAttributes(p, hdr.attributes); 792 cmsSetHeaderProfileID(p, (cmsUInt8Number*)&(hdr.profileID)); 793 cmsSetHeaderRenderingIntent(p, hdr.renderingIntent); 794 cmsSetPCS(p, hdr.pcs); 795 cmsSetColorSpace(p, hdr.colorSpace); 796 cmsSetDeviceClass(p, hdr.deviceClass); 797 cmsSetEncodedICCversion(p, hdr.version); 798 799 // now write the user supplied tag 800 if (size <= 0 || !cmsWriteRawTag(p, sig, pData, size)) { 801 cmsCloseProfile(p); 802 return NULL; 803 } 804 805 // copy tags from the original profile 806 for (i = 0; i < tagCount; i++) { 807 cmsBool isTagReady = FALSE; 808 const cmsTagSignature s = cmsGetTagSignature(pfTarget, i); 809 const cmsInt32Number tagSize = cmsReadRawTag(pfTarget, s, NULL, 0); 810 811 if (s == sig) { 812 // skip the user supplied tag 813 continue; 814 } 815 816 // read raw tag from the original profile 817 if (tagSize > 0) { 818 cmsUInt8Number* buf = (cmsUInt8Number*)malloc(tagSize); 819 if (buf != NULL) { 820 if (tagSize == cmsReadRawTag(pfTarget, s, buf, tagSize)) { 821 // now we are ready to write the tag 822 isTagReady = cmsWriteRawTag(p, s, buf, tagSize); 823 } 824 free(buf); 825 } 826 } 827 828 if (!isTagReady) { 829 cmsCloseProfile(p); 830 return NULL; 831 } 832 } 833 834 // now we have all tags moved to the new profile. 835 // do some sanity checks: write it to a memory buffer and read again. 836 if (cmsSaveProfileToMem(p, NULL, &pfSize)) { 837 void* buf = malloc(pfSize); 838 if (buf != NULL) { 839 // load raw profile data into the buffer 840 if (cmsSaveProfileToMem(p, buf, &pfSize)) { 841 pfSanity = cmsOpenProfileFromMem(buf, pfSize); 842 } 843 free(buf); 844 } 845 } 846 847 if (pfSanity == NULL) { 848 // for some reason, we failed to save and read the updated profile 849 // It likely indicates that the profile is not correct, so we report 850 // a failure here. 851 cmsCloseProfile(p); 852 p = NULL; 853 } else { 854 // do final check whether we can read and handle the the target tag. 855 const void* pTag = cmsReadTag(pfSanity, sig); 856 if (pTag == NULL) { 857 // the tag can not be cooked 858 cmsCloseProfile(p); 859 p = NULL; 860 } 861 cmsCloseProfile(pfSanity); 862 pfSanity = NULL; 863 } 864 865 return p; 866 }