LCMS.c revision 13029:55573c377d64
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
43static
44void 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
62static
63cmsInt32Number 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
98typedef struct lcmsProfile_s {
99    cmsHPROFILE pf;
100} lcmsProfile_t, *lcmsProfile_p;
101
102typedef union {
103    cmsTagSignature cms;
104    jint j;
105} TagSignature_t, *TagSignature_p;
106
107static jfieldID Trans_renderType_fID;
108static jfieldID Trans_ID_fID;
109static jfieldID IL_isIntPacked_fID;
110static jfieldID IL_dataType_fID;
111static jfieldID IL_pixelType_fID;
112static jfieldID IL_dataArray_fID;
113static jfieldID IL_offset_fID;
114static jfieldID IL_nextRowOffset_fID;
115static jfieldID IL_width_fID;
116static jfieldID IL_height_fID;
117static jfieldID IL_imageAtOnce_fID;
118
119JavaVM *javaVM;
120
121void 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
137JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *jvm, void *reserved) {
138    javaVM = jvm;
139
140    cmsSetLogErrorHandler(errorHandler);
141    return JNI_VERSION_1_6;
142}
143
144void 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
155void 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 */
167JNIEXPORT 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 */
254JNIEXPORT 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 */
317JNIEXPORT 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 */
337JNIEXPORT 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 */
379static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize);
380static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize);
381static 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 */
389JNIEXPORT 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 */
474JNIEXPORT 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
523void* 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
546void 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 */
570JNIEXPORT 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 */
642JNIEXPORT 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 */
676JNIEXPORT 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
731static 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
758static 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   */
786static 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}
895