Path: blob/master/src/java.desktop/share/native/liblcms/LCMS.c
66644 views
/*1* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <stdio.h>26#include <stdlib.h>27#include <memory.h>28#include "sun_java2d_cmm_lcms_LCMS.h"29#include "jni_util.h"30#include "Trace.h"31#include "Disposer.h"32#include <lcms2.h>33#include <lcms2_plugin.h>34#include "jlong.h"3536#define SigMake(a,b,c,d) \37( ( ((int) ((unsigned char) (a))) << 24) | \38( ((int) ((unsigned char) (b))) << 16) | \39( ((int) ((unsigned char) (c))) << 8) | \40(int) ((unsigned char) (d)))4142#define TagIdConst(a, b, c, d) \43((int) SigMake ((a), (b), (c), (d)))4445#define SigHead TagIdConst('h','e','a','d')4647#define DT_BYTE 048#define DT_SHORT 149#define DT_INT 250#define DT_DOUBLE 35152/* Default temp profile list size */53#define DF_ICC_BUF_SIZE 325455#define ERR_MSG_SIZE 2565657#ifdef _MSC_VER58# ifndef snprintf59# define snprintf _snprintf60# endif61#endif6263typedef struct lcmsProfile_s {64cmsHPROFILE pf;65} lcmsProfile_t, *lcmsProfile_p;6667typedef union {68cmsTagSignature cms;69jint j;70} TagSignature_t, *TagSignature_p;7172static jfieldID Trans_renderType_fID;73static jfieldID IL_isIntPacked_fID;74static jfieldID IL_dataType_fID;75static jfieldID IL_pixelType_fID;76static jfieldID IL_dataArray_fID;77static jfieldID IL_offset_fID;78static jfieldID IL_nextRowOffset_fID;79static jfieldID IL_width_fID;80static jfieldID IL_height_fID;81static jfieldID IL_imageAtOnce_fID;8283JavaVM *javaVM;8485void errorHandler(cmsContext ContextID, cmsUInt32Number errorCode,86const char *errorText) {87JNIEnv *env;88char errMsg[ERR_MSG_SIZE];8990int count = snprintf(errMsg, ERR_MSG_SIZE,91"LCMS error %d: %s", errorCode, errorText);92if (count < 0 || count >= ERR_MSG_SIZE) {93count = ERR_MSG_SIZE - 1;94}95errMsg[count] = 0;9697(*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL);98JNU_ThrowByName(env, "java/awt/color/CMMException", errMsg);99}100101JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *jvm, void *reserved) {102javaVM = jvm;103104cmsSetLogErrorHandler(errorHandler);105return JNI_VERSION_1_6;106}107108void LCMS_freeProfile(JNIEnv *env, jlong ptr) {109lcmsProfile_p p = (lcmsProfile_p)jlong_to_ptr(ptr);110111if (p != NULL) {112if (p->pf != NULL) {113cmsCloseProfile(p->pf);114}115free(p);116}117}118119void LCMS_freeTransform(JNIEnv *env, jlong ID)120{121cmsHTRANSFORM sTrans = jlong_to_ptr(ID);122/* Passed ID is always valid native ref so there is no check for zero */123cmsDeleteTransform(sTrans);124}125126/*127* Class: sun_java2d_cmm_lcms_LCMS128* Method: createNativeTransform129* Signature: ([JIIZIZLjava/lang/Object;)J130*/131JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform132(JNIEnv *env, jclass cls, jlongArray profileIDs, jint renderType,133jint inFormatter, jboolean isInIntPacked,134jint outFormatter, jboolean isOutIntPacked, jobject disposerRef)135{136cmsHPROFILE _iccArray[DF_ICC_BUF_SIZE];137cmsHPROFILE *iccArray = &_iccArray[0];138cmsHTRANSFORM sTrans = NULL;139int i, j, size;140jlong* ids;141142size = (*env)->GetArrayLength (env, profileIDs);143ids = (*env)->GetLongArrayElements(env, profileIDs, 0);144if (ids == NULL) {145// An exception should have already been thrown.146return 0L;147}148149#ifdef _LITTLE_ENDIAN150/* Reversing data packed into int for LE archs */151if (isInIntPacked) {152inFormatter ^= DOSWAP_SH(1);153}154if (isOutIntPacked) {155outFormatter ^= DOSWAP_SH(1);156}157#endif158159if (DF_ICC_BUF_SIZE < size*2) {160iccArray = (cmsHPROFILE*) malloc(161size*2*sizeof(cmsHPROFILE));162if (iccArray == NULL) {163(*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0);164165J2dRlsTraceLn(J2D_TRACE_ERROR, "getXForm: iccArray == NULL");166return 0L;167}168}169170j = 0;171for (i = 0; i < size; i++) {172cmsColorSpaceSignature cs;173lcmsProfile_p profilePtr = (lcmsProfile_p)jlong_to_ptr(ids[i]);174cmsHPROFILE icc = profilePtr->pf;175176iccArray[j++] = icc;177178/* Middle non-abstract profiles should be doubled before passing to179* the cmsCreateMultiprofileTransform function180*/181182cs = cmsGetColorSpace(icc);183if (size > 2 && i != 0 && i != size - 1 &&184cs != cmsSigXYZData && cs != cmsSigLabData)185{186iccArray[j++] = icc;187}188}189190sTrans = cmsCreateMultiprofileTransform(iccArray, j,191inFormatter, outFormatter, renderType, cmsFLAGS_COPY_ALPHA);192193(*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0);194195if (sTrans == NULL) {196J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_createNativeTransform: "197"sTrans == NULL");198if ((*env)->ExceptionOccurred(env) == NULL) {199JNU_ThrowByName(env, "java/awt/color/CMMException",200"Cannot get color transform");201}202} else {203Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, ptr_to_jlong(sTrans));204}205206if (iccArray != &_iccArray[0]) {207free(iccArray);208}209return ptr_to_jlong(sTrans);210}211212213/*214* Class: sun_java2d_cmm_lcms_LCMS215* Method: loadProfileNative216* Signature: ([BLjava/lang/Object;)J217*/218JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative219(JNIEnv *env, jclass cls, jbyteArray data, jobject disposerRef)220{221jbyte* dataArray;222jint dataSize;223lcmsProfile_p sProf = NULL;224cmsHPROFILE pf;225226if (JNU_IsNull(env, data)) {227JNU_ThrowIllegalArgumentException(env, "Invalid profile data");228return 0L;229}230231dataArray = (*env)->GetByteArrayElements (env, data, 0);232if (dataArray == NULL) {233// An exception should have already been thrown.234return 0L;235}236237dataSize = (*env)->GetArrayLength (env, data);238239pf = cmsOpenProfileFromMem((const void *)dataArray,240(cmsUInt32Number) dataSize);241242(*env)->ReleaseByteArrayElements (env, data, dataArray, 0);243244if (pf == NULL) {245JNU_ThrowIllegalArgumentException(env, "Invalid profile data");246} else {247/* Sanity check: try to save the profile in order248* to force basic validation.249*/250cmsUInt32Number pfSize = 0;251if (!cmsSaveProfileToMem(pf, NULL, &pfSize) ||252pfSize < sizeof(cmsICCHeader))253{254JNU_ThrowIllegalArgumentException(env, "Invalid profile data");255256cmsCloseProfile(pf);257pf = NULL;258}259}260261if (pf != NULL) {262// create profile holder263sProf = (lcmsProfile_p)malloc(sizeof(lcmsProfile_t));264if (sProf != NULL) {265// register the disposer record266sProf->pf = pf;267Disposer_AddRecord(env, disposerRef, LCMS_freeProfile, ptr_to_jlong(sProf));268} else {269cmsCloseProfile(pf);270}271}272273return ptr_to_jlong(sProf);274}275276/*277* Class: sun_java2d_cmm_lcms_LCMS278* Method: getProfileDataNative279* Signature: (J)[B280*/281JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileDataNative282(JNIEnv *env, jclass cls, jlong id)283{284lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id);285cmsUInt32Number pfSize = 0;286287// determine actual profile size288if (!cmsSaveProfileToMem(sProf->pf, NULL, &pfSize)) {289JNU_ThrowByName(env, "java/awt/color/CMMException",290"Can not access specified profile.");291return NULL;292}293294jbyteArray data = (*env)->NewByteArray(env, pfSize);295if (data == NULL) {296// An exception should have already been thrown.297return NULL;298}299300jbyte* dataArray = (*env)->GetByteArrayElements(env, data, 0);301if (dataArray == NULL) {302// An exception should have already been thrown.303return NULL;304}305306cmsBool status = cmsSaveProfileToMem(sProf->pf, dataArray, &pfSize);307308(*env)->ReleaseByteArrayElements(env, data, dataArray, 0);309310if (!status) {311JNU_ThrowByName(env, "java/awt/color/CMMException",312"Can not access specified profile.");313return NULL;314}315return data;316}317318/* Get profile header info */319static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize);320static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize);321static cmsHPROFILE _writeCookedTag(cmsHPROFILE pfTarget, cmsTagSignature sig, jbyte *pData, jint size);322323324/*325* Class: sun_java2d_cmm_lcms_LCMS326* Method: getTagNative327* Signature: (JI)[B328*/329JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagNative330(JNIEnv *env, jclass cls, jlong id, jint tagSig)331{332lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id);333TagSignature_t sig;334cmsUInt32Number tagSize;335336jbyte* dataArray = NULL;337jbyteArray data = NULL;338339cmsUInt32Number bufSize;340341sig.j = tagSig;342343if (tagSig == SigHead) {344cmsBool status;345346// allocate java array347bufSize = sizeof(cmsICCHeader);348data = (*env)->NewByteArray(env, bufSize);349350if (data == NULL) {351// An exception should have already been thrown.352return NULL;353}354355dataArray = (*env)->GetByteArrayElements (env, data, 0);356357if (dataArray == NULL) {358// An exception should have already been thrown.359return NULL;360}361362status = _getHeaderInfo(sProf->pf, dataArray, bufSize);363364(*env)->ReleaseByteArrayElements (env, data, dataArray, 0);365366if (!status) {367JNU_ThrowByName(env, "java/awt/color/CMMException",368"ICC Profile header not found");369return NULL;370}371372return data;373}374375if (cmsIsTag(sProf->pf, sig.cms)) {376tagSize = cmsReadRawTag(sProf->pf, sig.cms, NULL, 0);377} else {378JNU_ThrowByName(env, "java/awt/color/CMMException",379"ICC profile tag not found");380return NULL;381}382383// allocate java array384data = (*env)->NewByteArray(env, tagSize);385if (data == NULL) {386// An exception should have already been thrown.387return NULL;388}389390dataArray = (*env)->GetByteArrayElements (env, data, 0);391392if (dataArray == NULL) {393// An exception should have already been thrown.394return NULL;395}396397bufSize = cmsReadRawTag(sProf->pf, sig.cms, dataArray, tagSize);398399(*env)->ReleaseByteArrayElements (env, data, dataArray, 0);400401if (bufSize != tagSize) {402JNU_ThrowByName(env, "java/awt/color/CMMException",403"Can not get tag data.");404return NULL;405}406return data;407}408409/*410* Class: sun_java2d_cmm_lcms_LCMS411* Method: setTagDataNative412* Signature: (JI[B)V413*/414JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagDataNative415(JNIEnv *env, jclass cls, jlong id, jint tagSig, jbyteArray data)416{417lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id);418cmsHPROFILE pfReplace = NULL;419420TagSignature_t sig;421cmsBool status = FALSE;422jbyte* dataArray;423int tagSize;424425sig.j = tagSig;426427if (JNU_IsNull(env, data)) {428JNU_ThrowIllegalArgumentException(env, "Can not write tag data.");429return;430}431432tagSize =(*env)->GetArrayLength(env, data);433434dataArray = (*env)->GetByteArrayElements(env, data, 0);435436if (dataArray == NULL) {437// An exception should have already been thrown.438return;439}440441if (tagSig == SigHead) {442status = _setHeaderInfo(sProf->pf, dataArray, tagSize);443} else {444/*445* New strategy for generic tags: create a place holder,446* dump all existing tags there, dump externally supplied447* tag, and return the new profile to the java.448*/449pfReplace = _writeCookedTag(sProf->pf, sig.cms, dataArray, tagSize);450status = (pfReplace != NULL);451}452453(*env)->ReleaseByteArrayElements(env, data, dataArray, 0);454455if (!status) {456JNU_ThrowIllegalArgumentException(env, "Can not write tag data.");457} else if (pfReplace != NULL) {458cmsCloseProfile(sProf->pf);459sProf->pf = pfReplace;460}461}462463void* getILData (JNIEnv *env, jobject img, jint* pDataType,464jobject* pDataObject) {465void* result = NULL;466*pDataType = (*env)->GetIntField (env, img, IL_dataType_fID);467*pDataObject = (*env)->GetObjectField(env, img, IL_dataArray_fID);468switch (*pDataType) {469case DT_BYTE:470result = (*env)->GetByteArrayElements (env, *pDataObject, 0);471break;472case DT_SHORT:473result = (*env)->GetShortArrayElements (env, *pDataObject, 0);474break;475case DT_INT:476result = (*env)->GetIntArrayElements (env, *pDataObject, 0);477break;478case DT_DOUBLE:479result = (*env)->GetDoubleArrayElements (env, *pDataObject, 0);480break;481}482483return result;484}485486void releaseILData (JNIEnv *env, void* pData, jint dataType,487jobject dataObject) {488switch (dataType) {489case DT_BYTE:490(*env)->ReleaseByteArrayElements(env,dataObject,(jbyte*)pData,0);491break;492case DT_SHORT:493(*env)->ReleaseShortArrayElements(env,dataObject,(jshort*)pData, 0);494break;495case DT_INT:496(*env)->ReleaseIntArrayElements(env,dataObject,(jint*)pData,0);497break;498case DT_DOUBLE:499(*env)->ReleaseDoubleArrayElements(env,dataObject,(jdouble*)pData,5000);501break;502}503}504505/*506* Class: sun_java2d_cmm_lcms_LCMS507* Method: colorConvert508* Signature: (Lsun/java2d/cmm/lcms/LCMSTransform;Lsun/java2d/cmm/lcms/LCMSImageLayout;Lsun/java2d/cmm/lcms/LCMSImageLayout;)V509*/510JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert511(JNIEnv *env, jclass cls, jlong ID, jobject src, jobject dst)512{513cmsHTRANSFORM sTrans = jlong_to_ptr(ID);514int srcDType, dstDType;515int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset;516int width, height, i;517void* inputBuffer;518void* outputBuffer;519char* inputRow;520char* outputRow;521jobject srcData, dstData;522jboolean srcAtOnce = JNI_FALSE, dstAtOnce = JNI_FALSE;523524srcOffset = (*env)->GetIntField (env, src, IL_offset_fID);525srcNextRowOffset = (*env)->GetIntField (env, src, IL_nextRowOffset_fID);526dstOffset = (*env)->GetIntField (env, dst, IL_offset_fID);527dstNextRowOffset = (*env)->GetIntField (env, dst, IL_nextRowOffset_fID);528width = (*env)->GetIntField (env, src, IL_width_fID);529height = (*env)->GetIntField (env, src, IL_height_fID);530531srcAtOnce = (*env)->GetBooleanField(env, src, IL_imageAtOnce_fID);532dstAtOnce = (*env)->GetBooleanField(env, dst, IL_imageAtOnce_fID);533534if (sTrans == NULL) {535J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL");536JNU_ThrowByName(env, "java/awt/color/CMMException",537"Cannot get color transform");538return;539}540541542inputBuffer = getILData (env, src, &srcDType, &srcData);543544if (inputBuffer == NULL) {545J2dRlsTraceLn(J2D_TRACE_ERROR, "");546// An exception should have already been thrown.547return;548}549550outputBuffer = getILData (env, dst, &dstDType, &dstData);551552if (outputBuffer == NULL) {553releaseILData(env, inputBuffer, srcDType, srcData);554// An exception should have already been thrown.555return;556}557558inputRow = (char*)inputBuffer + srcOffset;559outputRow = (char*)outputBuffer + dstOffset;560561if (srcAtOnce && dstAtOnce) {562cmsDoTransform(sTrans, inputRow, outputRow, width * height);563} else {564for (i = 0; i < height; i++) {565cmsDoTransform(sTrans, inputRow, outputRow, width);566inputRow += srcNextRowOffset;567outputRow += dstNextRowOffset;568}569}570571releaseILData(env, inputBuffer, srcDType, srcData);572releaseILData(env, outputBuffer, dstDType, dstData);573}574575/*576* Class: sun_java2d_cmm_lcms_LCMS577* Method: getProfileID578* Signature: (Ljava/awt/color/ICC_Profile;)Lsun/java2d/cmm/lcms/LCMSProfile;579*/580JNIEXPORT jobject JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileID581(JNIEnv *env, jclass cls, jobject pf)582{583if (pf == NULL) {584return NULL;585}586jclass pcls = (*env)->GetObjectClass(env, pf);587if (pcls == NULL) {588return NULL;589}590jmethodID mid = (*env)->GetMethodID(env, pcls, "cmmProfile",591"()Lsun/java2d/cmm/Profile;");592if (mid == NULL) {593return NULL;594}595jobject cmmProfile = (*env)->CallObjectMethod(env, pf, mid);596if ((*env)->ExceptionOccurred(env)) {597return NULL;598}599jclass lcmsPCls = (*env)->FindClass(env, "sun/java2d/cmm/lcms/LCMSProfile");600if (lcmsPCls == NULL) {601return NULL;602}603if ((*env)->IsInstanceOf(env, cmmProfile, lcmsPCls)) {604return cmmProfile;605}606return NULL;607}608609/*610* Class: sun_java2d_cmm_lcms_LCMS611* Method: initLCMS612* Signature: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)V613*/614JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_initLCMS615(JNIEnv *env, jclass cls, jclass Trans, jclass IL, jclass Pf)616{617/* TODO: move initialization of the IDs to the static blocks of618* corresponding classes to avoid problems with invalidating ids by class619* unloading620*/621Trans_renderType_fID = (*env)->GetFieldID (env, Trans, "renderType", "I");622if (Trans_renderType_fID == NULL) {623return;624}625IL_isIntPacked_fID = (*env)->GetFieldID (env, IL, "isIntPacked", "Z");626if (IL_isIntPacked_fID == NULL) {627return;628}629IL_dataType_fID = (*env)->GetFieldID (env, IL, "dataType", "I");630if (IL_dataType_fID == NULL) {631return;632}633IL_pixelType_fID = (*env)->GetFieldID (env, IL, "pixelType", "I");634if (IL_pixelType_fID == NULL) {635return;636}637IL_dataArray_fID = (*env)->GetFieldID(env, IL, "dataArray",638"Ljava/lang/Object;");639if (IL_dataArray_fID == NULL) {640return;641}642IL_width_fID = (*env)->GetFieldID (env, IL, "width", "I");643if (IL_width_fID == NULL) {644return;645}646IL_height_fID = (*env)->GetFieldID (env, IL, "height", "I");647if (IL_height_fID == NULL) {648return;649}650IL_offset_fID = (*env)->GetFieldID (env, IL, "offset", "I");651if (IL_offset_fID == NULL) {652return;653}654IL_imageAtOnce_fID = (*env)->GetFieldID (env, IL, "imageAtOnce", "Z");655if (IL_imageAtOnce_fID == NULL) {656return;657}658IL_nextRowOffset_fID = (*env)->GetFieldID (env, IL, "nextRowOffset", "I");659if (IL_nextRowOffset_fID == NULL) {660return;661}662}663664static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize)665{666cmsUInt32Number pfSize = 0;667cmsUInt8Number* pfBuffer = NULL;668cmsBool status = FALSE;669670if (!cmsSaveProfileToMem(pf, NULL, &pfSize) ||671pfSize < sizeof(cmsICCHeader) ||672bufferSize < (jint)sizeof(cmsICCHeader))673{674return FALSE;675}676677pfBuffer = malloc(pfSize);678if (pfBuffer == NULL) {679return FALSE;680}681682// load raw profile data into the buffer683if (cmsSaveProfileToMem(pf, pfBuffer, &pfSize)) {684memcpy(pBuffer, pfBuffer, sizeof(cmsICCHeader));685status = TRUE;686}687free(pfBuffer);688return status;689}690691static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize)692{693cmsICCHeader pfHeader;694695if (pBuffer == NULL || bufferSize < (jint)sizeof(cmsICCHeader)) {696return FALSE;697}698699memcpy(&pfHeader, pBuffer, sizeof(cmsICCHeader));700701// now set header fields, which we can access using the lcms2 public API702cmsSetHeaderFlags(pf, _cmsAdjustEndianess32(pfHeader.flags));703cmsSetHeaderManufacturer(pf, _cmsAdjustEndianess32(pfHeader.manufacturer));704cmsSetHeaderModel(pf, _cmsAdjustEndianess32(pfHeader.model));705cmsUInt64Number attributes;706_cmsAdjustEndianess64(&attributes, &pfHeader.attributes);707cmsSetHeaderAttributes(pf, attributes);708cmsSetHeaderProfileID(pf, (cmsUInt8Number*)&(pfHeader.profileID));709cmsSetHeaderRenderingIntent(pf, _cmsAdjustEndianess32(pfHeader.renderingIntent));710cmsSetPCS(pf, _cmsAdjustEndianess32(pfHeader.pcs));711cmsSetColorSpace(pf, _cmsAdjustEndianess32(pfHeader.colorSpace));712cmsSetDeviceClass(pf, _cmsAdjustEndianess32(pfHeader.deviceClass));713cmsSetEncodedICCversion(pf, _cmsAdjustEndianess32(pfHeader.version));714715return TRUE;716}717718/* Returns new profile handler, if it was created successfully,719NULL otherwise.720*/721static cmsHPROFILE _writeCookedTag(const cmsHPROFILE pfTarget,722const cmsTagSignature sig,723jbyte *pData, jint size)724{725cmsUInt32Number pfSize = 0;726const cmsInt32Number tagCount = cmsGetTagCount(pfTarget);727cmsInt32Number i;728cmsHPROFILE pfSanity = NULL;729730cmsICCHeader hdr;731732cmsHPROFILE p = cmsCreateProfilePlaceholder(NULL);733734if (NULL == p) {735return NULL;736}737memset(&hdr, 0, sizeof(cmsICCHeader));738739// Populate the placeholder's header according to target profile740hdr.flags = cmsGetHeaderFlags(pfTarget);741hdr.renderingIntent = cmsGetHeaderRenderingIntent(pfTarget);742hdr.manufacturer = cmsGetHeaderManufacturer(pfTarget);743hdr.model = cmsGetHeaderModel(pfTarget);744hdr.pcs = cmsGetPCS(pfTarget);745hdr.colorSpace = cmsGetColorSpace(pfTarget);746hdr.deviceClass = cmsGetDeviceClass(pfTarget);747hdr.version = cmsGetEncodedICCversion(pfTarget);748cmsGetHeaderAttributes(pfTarget, &hdr.attributes);749cmsGetHeaderProfileID(pfTarget, (cmsUInt8Number*)&hdr.profileID);750751cmsSetHeaderFlags(p, hdr.flags);752cmsSetHeaderManufacturer(p, hdr.manufacturer);753cmsSetHeaderModel(p, hdr.model);754cmsSetHeaderAttributes(p, hdr.attributes);755cmsSetHeaderProfileID(p, (cmsUInt8Number*)&(hdr.profileID));756cmsSetHeaderRenderingIntent(p, hdr.renderingIntent);757cmsSetPCS(p, hdr.pcs);758cmsSetColorSpace(p, hdr.colorSpace);759cmsSetDeviceClass(p, hdr.deviceClass);760cmsSetEncodedICCversion(p, hdr.version);761762// now write the user supplied tag763if (size <= 0 || !cmsWriteRawTag(p, sig, pData, size)) {764cmsCloseProfile(p);765return NULL;766}767768// copy tags from the original profile769for (i = 0; i < tagCount; i++) {770cmsBool isTagReady = FALSE;771const cmsTagSignature s = cmsGetTagSignature(pfTarget, i);772const cmsUInt32Number tagSize = cmsReadRawTag(pfTarget, s, NULL, 0);773774if (s == sig) {775// skip the user supplied tag776continue;777}778779// read raw tag from the original profile780if (tagSize > 0) {781cmsUInt8Number* buf = (cmsUInt8Number*)malloc(tagSize);782if (buf != NULL) {783if (tagSize == cmsReadRawTag(pfTarget, s, buf, tagSize)) {784// now we are ready to write the tag785isTagReady = cmsWriteRawTag(p, s, buf, tagSize);786}787free(buf);788}789}790791if (!isTagReady) {792cmsCloseProfile(p);793return NULL;794}795}796797// now we have all tags moved to the new profile.798// do some sanity checks: write it to a memory buffer and read again.799if (cmsSaveProfileToMem(p, NULL, &pfSize)) {800void* buf = malloc(pfSize);801if (buf != NULL) {802// load raw profile data into the buffer803if (cmsSaveProfileToMem(p, buf, &pfSize)) {804pfSanity = cmsOpenProfileFromMem(buf, pfSize);805}806free(buf);807}808}809810if (pfSanity == NULL) {811// for some reason, we failed to save and read the updated profile812// It likely indicates that the profile is not correct, so we report813// a failure here.814cmsCloseProfile(p);815p = NULL;816} else {817// do final check whether we can read and handle the target tag.818const void* pTag = cmsReadTag(pfSanity, sig);819if (pTag == NULL) {820// the tag can not be cooked821cmsCloseProfile(p);822p = NULL;823}824cmsCloseProfile(pfSanity);825pfSanity = NULL;826}827828return p;829}830831832