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 }