src/java.desktop/share/native/liblcms/LCMS.c
changeset 47216 71c04702a3d5
parent 33653 c1ee09fe3274
child 48566 6c986cf7299a
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2007, 2015, 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 DEF_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 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 }