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