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 }