Example #1
0
File: LCMS.c Project: frohoff/jdk6
/*
 * Class:     sun_java2d_cmm_lcms_LCMS
 * Method:    createNativeTransform
 * Signature: ([JI)J
 */
JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
  (JNIEnv *env, jclass cls, jlongArray profileIDs, jint renderType,
   jobject disposerRef)
{
    LPLCMSICCPROFILE _iccArray[DF_ICC_BUF_SIZE];
    LPLCMSICCPROFILE *iccArray = &_iccArray[0];
    cmsHTRANSFORM transform;
    storeID_t sTrans;
    int i, j, size;
    jlong* ids;

    size = (*env)->GetArrayLength (env, profileIDs);
    ids = (*env)->GetPrimitiveArrayCritical(env, profileIDs, 0);

    if (DF_ICC_BUF_SIZE < size*2) {
        iccArray = (LPLCMSICCPROFILE*) malloc(
            size*2*sizeof(LPLCMSICCPROFILE));
        if (iccArray == NULL) {
            J2dRlsTraceLn(J2D_TRACE_ERROR, "getXForm: iccArray == NULL");
            return NULL;
        }
    }

    j = 0;
    for (i = 0; i < size; i++) {
        LPLCMSICCPROFILE icc;
        sTrans.j = ids[i];
        icc = sTrans.pf;
        iccArray[j++] = icc;

        /* Middle non-abstract profiles should be doubled before passing to
         * the cmsCreateMultiprofileTransform function
         */
        if (size > 2 && i != 0 && i != size - 1 &&
            icc->ColorSpace != icSigXYZData &&
            icc->ColorSpace != icSigLabData)
        {
            iccArray[j++] = icc;
        }
    }

    sTrans.xf = cmsCreateMultiprofileTransform(iccArray, j,
        0, 0, renderType, 0);

    (*env)->ReleasePrimitiveArrayCritical(env, profileIDs, ids, 0);

    if (sTrans.xf == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_createNativeTransform: "
                                       "sTrans.xf == NULL");
        JNU_ThrowByName(env, "java/awt/color/CMMException",
                        "Cannot get color transform");
    } else {
        Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, sTrans.j);
    }
    if (iccArray != &_iccArray[0]) {
        free(iccArray);
    }
    return sTrans.j;
}
Example #2
0
/* Get the link from the CMS, but include proofing and/or a device link  
   profile. */
gcmmhlink_t
gscms_get_link_proof_devlink(gcmmhprofile_t lcms_srchandle,
                             gcmmhprofile_t lcms_proofhandle,
                             gcmmhprofile_t lcms_deshandle, 
                             gcmmhprofile_t lcms_devlinkhandle, 
                             gsicc_rendering_param_t *rendering_params,
                             bool src_dev_link, int cmm_flags,
                             gs_memory_t *mem)
{
    DWORD src_data_type,des_data_type;
    icColorSpaceSignature src_color_space,des_color_space;
    int src_nChannels,des_nChannels;
    int lcms_src_color_space, lcms_des_color_space;
    cmsHPROFILE hProfiles[5]; 
    int nProfiles = 0;

   /* First handle all the source stuff */
    src_color_space  = cmsGetColorSpace(lcms_srchandle);
    lcms_src_color_space = _cmsLCMScolorSpace(src_color_space);
    /* littlecms returns -1 for types it does not (but should) understand */
    if (lcms_src_color_space < 0) lcms_src_color_space = 0;
    src_nChannels = _cmsChannelsOf(src_color_space);
    /* For now, just do single byte data, interleaved.  We can change this
      when we use the transformation. */
    src_data_type = (COLORSPACE_SH(lcms_src_color_space)|
                        CHANNELS_SH(src_nChannels)|BYTES_SH(2));    
    if (lcms_deshandle != NULL) {
        des_color_space  = cmsGetColorSpace(lcms_deshandle);
    } else {
        /* We must have a device link profile. */
        des_color_space = cmsGetPCS(lcms_deshandle);
    }
    lcms_des_color_space = _cmsLCMScolorSpace(des_color_space);
    if (lcms_des_color_space < 0) lcms_des_color_space = 0;
    des_nChannels = _cmsChannelsOf(des_color_space);
    des_data_type = (COLORSPACE_SH(lcms_des_color_space)|
                        CHANNELS_SH(des_nChannels)|BYTES_SH(2));
    /* lcms proofing transform has a clunky API and can't include the device 
       link profile if we have both. So use cmsCreateMultiprofileTransform 
       instead and round trip the proofing profile. */
    hProfiles[nProfiles++] = lcms_srchandle;
    if (lcms_proofhandle != NULL) {
        hProfiles[nProfiles++] = lcms_proofhandle;
        hProfiles[nProfiles++] = lcms_proofhandle;
    }
    hProfiles[nProfiles++] = lcms_deshandle;
    if (lcms_devlinkhandle != NULL) {
        hProfiles[nProfiles++] = lcms_devlinkhandle;
    }
    return(cmsCreateMultiprofileTransform(hProfiles, nProfiles, src_data_type, 
                                          des_data_type, rendering_params->rendering_intent, 
                                          (cmm_flags | cmsFLAGS_BLACKPOINTCOMPENSATION | 
                                           cmsFLAGS_HIGHRESPRECALC |
                                           cmsFLAGS_NOTCACHE)));
}
Example #3
0
/******************************************************************************
 * CreateMultiProfileTransform      [MSCMS.@]
 *
 * Create a color transform from an array of color profiles.
 *
 * PARAMS
 *  profiles  [I] Array of color profiles.
 *  nprofiles [I] Number of color profiles.
 *  intents   [I] Array of rendering intents.
 *  flags     [I] Flags.
 *  cmm       [I] Profile to take the CMM from.
 *
 * RETURNS
 *  Success: Handle to a transform.
 *  Failure: NULL
 */ 
HTRANSFORM WINAPI CreateMultiProfileTransform( PHPROFILE profiles, DWORD nprofiles,
    PDWORD intents, DWORD nintents, DWORD flags, DWORD cmm )
{
    HTRANSFORM ret = NULL;
#ifdef HAVE_LCMS2
    cmsHPROFILE *cmsprofiles;
    struct transform transform;
    struct profile *profile0, *profile1;

    TRACE( "( %p, 0x%08x, %p, 0x%08x, 0x%08x, 0x%08x )\n",
           profiles, nprofiles, intents, nintents, flags, cmm );

    if (!profiles || !nprofiles || !intents) return NULL;

    if (nprofiles > 2)
    {
        FIXME("more than 2 profiles not supported\n");
        return NULL;
    }

    profile0 = grab_profile( profiles[0] );
    if (!profile0) return NULL;
    profile1 = grab_profile( profiles[1] );
    if (!profile1)
    {
        release_profile( profile0 );
        return NULL;
    }

    if ((cmsprofiles = HeapAlloc( GetProcessHeap(), 0, (nprofiles + 1) * sizeof(cmsHPROFILE) )))
    {
        cmsprofiles[0] = profile0->cmsprofile;
        cmsprofiles[1] = profile1->cmsprofile;

        transform.cmstransform = cmsCreateMultiprofileTransform( cmsprofiles, nprofiles, 0,
                                                                 0, *intents, 0 );
        HeapFree( GetProcessHeap(), 0, cmsprofiles );
        if (!transform.cmstransform)
        {
            release_profile( profile0 );
            release_profile( profile1 );
            return FALSE;
        }
        ret = create_transform( &transform );
    }

    release_profile( profile0 );
    release_profile( profile1 );

#endif /* HAVE_LCMS2 */
    return ret;
}
Example #4
0
static
int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
{
    cmsHPROFILE hLab;
    cmsHTRANSFORM xform;
    cmsUInt32Number nChannels;
    cmsUInt32Number InputFormat;
    int rc;
    cmsHPROFILE Profiles[2];
    cmsCIEXYZ BlackPointAdaptedToD50;

    // Does create a device-link based transform. 
    // The DeviceLink is next dumped as working CSA.
    
    InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
    nChannels   = T_CHANNELS(InputFormat);

	
	cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);

 	// Adjust output to Lab4 
    hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);

	Profiles[0] = hProfile;
	Profiles[1] = hLab;

	xform = cmsCreateMultiprofileTransform(Profiles, 2,  InputFormat, TYPE_Lab_DBL, Intent, 0);
	cmsCloseProfile(hLab);
	
	if (xform == NULL) {

		cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
		return 0;
	}
    
    // Only 1, 3 and 4 channels are allowed

    switch (nChannels) {

    case 1: {            
		    cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
            EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);            
            cmsFreeToneCurve(Gray2Y);            
            }
            break;

    case 3: 
    case 4: {
		    cmsUInt32Number OutFrm = TYPE_Lab_16;
            cmsPipeline* DeviceLink;
            _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;

			DeviceLink = cmsPipelineDup(v ->Lut);
			if (DeviceLink == NULL) return 0;

			dwFlags |= cmsFLAGS_FORCE_CLUT;
			_cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
            
            rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
            cmsPipelineFree(DeviceLink);            
            }
            break;

    default:

		cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
        return 0;
    }
    

    cmsDeleteTransform(xform);
    
    return 1;
}
Example #5
0
static
int TransformImage(TIFF* in, TIFF* out, const char *cDefInpProf, const char *cOutProf)
{
       cmsHPROFILE hIn, hOut, hProof, hInkLimit = NULL;
       cmsHTRANSFORM xform;
       DWORD wInput, wOutput;
       int OutputColorSpace;
       int bps = (Width16 ? 2 : 1);
       DWORD dwFlags = 0;        
       int nPlanes;

    // Observer adaptation state (only meaningful on absolute colorimetric intent)

       cmsSetAdaptationState(ObserverAdaptationState);

       if (EmbedProfile && cOutProf) 
           DoEmbedProfile(out, cOutProf);


       
       if (BlackWhiteCompensation) 
            dwFlags |= cmsFLAGS_WHITEBLACKCOMPENSATION;           
       

       if (PreserveBlack) {
			dwFlags |= cmsFLAGS_PRESERVEBLACK;
			if (PrecalcMode == 0) PrecalcMode = 1;
	   }

       switch (PrecalcMode) {
           
       case 0: dwFlags |= cmsFLAGS_NOTPRECALC; break;
       case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
       case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
       case 1: break;

       default: FatalError("Unknown precalculation mode '%d'", PrecalcMode);
       }
        

       if (GamutCheck)
            dwFlags |= cmsFLAGS_GAMUTCHECK;
        

       hProof = NULL;
       hOut = NULL;

       if (lIsDeviceLink) {

            hIn = cmsOpenProfileFromFile(cDefInpProf, "r");                  
       }
       else {

               hIn =  GetTIFFProfile(in);

               if (hIn == NULL)                    
                       hIn = OpenStockProfile(cDefInpProf);               
               
               hOut = OpenStockProfile(cOutProf);
                 
               if (cProofing != NULL) {

                   hProof = OpenStockProfile(cProofing);
                   dwFlags |= cmsFLAGS_SOFTPROOFING;
               }
       }

       // Take input color space

       wInput = GetInputPixelType(in);

       // Assure both, input profile and input TIFF are on same colorspace

       if (_cmsLCMScolorSpace(cmsGetColorSpace(hIn)) != (int) T_COLORSPACE(wInput))
              FatalError("Input profile is not operating in proper color space");

      
       if (!lIsDeviceLink) 
                OutputColorSpace = _cmsLCMScolorSpace(cmsGetColorSpace(hOut));
       else 
                OutputColorSpace = _cmsLCMScolorSpace(cmsGetPCS(hIn));
                
       wOutput      = ComputeOutputFormatDescriptor(wInput, OutputColorSpace, bps);

       WriteOutputTags(out, OutputColorSpace, bps);
       CopyOtherTags(in, out);

       // Ink limit
       if (InkLimit != 400.0 && 
                (OutputColorSpace == PT_CMYK || OutputColorSpace == PT_CMY)) {

           cmsHPROFILE hProfiles[10];
           int nProfiles = 0;


           hInkLimit = cmsCreateInkLimitingDeviceLink(cmsGetColorSpace(hOut), InkLimit);

           hProfiles[nProfiles++] = hIn;
           if (hProof) {
                hProfiles[nProfiles++] = hProof;
                hProfiles[nProfiles++] = hProof;
           }

           hProfiles[nProfiles++] = hOut;
           hProfiles[nProfiles++] = hInkLimit;
                   
           xform = cmsCreateMultiprofileTransform(hProfiles, nProfiles, 
                                                wInput, wOutput, Intent, dwFlags);
           
       }
       else {

		   xform = cmsCreateProofingTransform(hIn, wInput, 
											  hOut, wOutput, 
											  hProof, Intent, 
											  ProofingIntent, 
											  dwFlags);
       }

      

       // Planar stuff

       if (T_PLANAR(wInput)) 
            nPlanes = T_CHANNELS(wInput) + T_EXTRA(wInput);
       else
            nPlanes = 1;


	   // TIFF Lab of 8 bits need special handling

		if (wInput == TYPE_Lab_8 && 
			   !InputLabUsingICC &&
			   cInpProf != NULL  &&
			   stricmp(cInpProf, "*Lab") == 0) {

					cmsSetUserFormatters(xform, TYPE_Lab_8, UnrollTIFFLab8, TYPE_Lab_8, NULL); 
		}


		if (wOutput == TYPE_Lab_8 && 			   
			   cOutProf != NULL  &&
			   stricmp(cOutProf, "*Lab") == 0) {

					cmsSetUserFormatters(xform, TYPE_Lab_8, NULL, TYPE_Lab_8, PackTIFFLab8); 
		}

	   
       // Handle tile by tile or strip by strip

       if (TIFFIsTiled(in)) {

                TileBasedXform(xform, in, out, nPlanes);
       }
       else {

                StripBasedXform(xform, in, out, nPlanes);
       }


       cmsDeleteTransform(xform);
       cmsCloseProfile(hIn);
       cmsCloseProfile(hOut);
       if (hInkLimit) 
           cmsCloseProfile(hInkLimit);
       if (hProof) 
           cmsCloseProfile(hProof);


       TIFFWriteDirectory(out);

       return 1;
}
Example #6
0
/******************************************************************************
 * CreateMultiProfileTransform      [MSCMS.@]
 *
 * Create a color transform from an array of color profiles.
 *
 * PARAMS
 *  profiles  [I] Array of color profiles.
 *  nprofiles [I] Number of color profiles.
 *  intents   [I] Array of rendering intents.
 *  flags     [I] Flags.
 *  cmm       [I] Profile to take the CMM from.
 *
 * RETURNS
 *  Success: Handle to a transform.
 *  Failure: NULL
 */ 
HTRANSFORM WINAPI CreateMultiProfileTransform( PHPROFILE profiles, DWORD nprofiles,
    PDWORD intents, DWORD nintents, DWORD flags, DWORD cmm )
{
    HTRANSFORM ret = NULL;
#ifdef HAVE_LCMS
    cmsHPROFILE *cmsprofiles, cmsconvert = NULL;
    struct transform transform;
    struct profile *profile0, *profile1;
    DWORD in_format, out_format;

    TRACE( "( %p, 0x%08x, %p, 0x%08x, 0x%08x, 0x%08x )\n",
           profiles, nprofiles, intents, nintents, flags, cmm );

    if (!profiles || !nprofiles || !intents) return NULL;

    if (nprofiles > 2)
    {
        FIXME("more than 2 profiles not supported\n");
        return NULL;
    }

    profile0 = grab_profile( profiles[0] );
    if (!profile0) return NULL;
    profile1 = grab_profile( profiles[1] );
    if (!profile1)
    {
        release_profile( profile0 );
        return NULL;
    }
    in_format  = from_profile( profiles[0] );
    out_format = from_profile( profiles[nprofiles - 1] );

    if (in_format != out_format)
    {
        /* insert a conversion profile for pairings that lcms doesn't handle */
        if (out_format == TYPE_RGB_16) cmsconvert = cmsCreate_sRGBProfile();
        if (out_format == TYPE_Lab_16) cmsconvert = cmsCreateLabProfile( NULL );
    }

    cmsprofiles = HeapAlloc( GetProcessHeap(), 0, (nprofiles + 1) * sizeof(cmsHPROFILE) );
    if (cmsprofiles)
    {
        cmsprofiles[0] = profile0->cmsprofile;
        if (cmsconvert)
        {
            cmsprofiles[1] = cmsconvert;
            cmsprofiles[2] = profile1->cmsprofile;
            nprofiles++;
        }
        else
        {
            cmsprofiles[1] = profile1->cmsprofile;
        }
        transform.cmstransform = cmsCreateMultiprofileTransform( cmsprofiles, nprofiles, in_format, out_format, *intents, 0 );

        HeapFree( GetProcessHeap(), 0, cmsprofiles );
        ret = create_transform( &transform );
    }

    release_profile( profile0 );
    release_profile( profile1 );

#endif /* HAVE_LCMS */
    return ret;
}
Example #7
0
int main(int argc, char *argv[])
{
	int i, nargs;
	cmsHPROFILE Profiles[257];
	cmsHPROFILE hProfile;
	DWORD dwFlags = 0;
	cmsHTRANSFORM hTransform;
    

     fprintf(stderr, "little cms device link generator - v1.7\n");

	 HandleSwitches(argc, argv);

     cmsSetErrorHandler(MyErrorHandler);

     nargs = (argc - xoptind);
	 if (nargs < 1)
				Help(0); 
	 
	 if (nargs > 255)
			FatalError("ERROR: Holy profile! what are you trying to do with so many profiles?");


	 for (i=0; i < nargs; i++) {
		 Profiles[i] = OpenProfile(argv[i + xoptind]);
	 }

	

	 switch (PrecalcMode) {
           	
	    case 0: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
		case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
		case 1: 
            if (NumOfGridPoints > 0)
                dwFlags |= cmsFLAGS_GRIDPOINTS(NumOfGridPoints);
            break;

		default: FatalError("ERROR: Unknown precalculation mode '%d'", PrecalcMode);
	 }

     if (BlackPointCompensation)
            dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;

     if (BlackPreservation > 0) {

            dwFlags |= cmsFLAGS_PRESERVEBLACK;
            cmsSetCMYKPreservationStrategy(BlackPreservation-1);
     }

     if (TagResult)
            dwFlags |= cmsFLAGS_GUESSDEVICECLASS;

     if (NoPrelinearization)
         dwFlags |= cmsFLAGS_NOPRELINEARIZATION;
            
     if (InkLimit != 400.0) {

            cmsHPROFILE hInkLimit = cmsCreateInkLimitingDeviceLink(
                                    cmsGetColorSpace(Profiles[nargs-1]), InkLimit);

            Profiles[nargs++] = hInkLimit;
     }

     if (lUse8bits) dwFlags |= cmsFLAGS_NOPRELINEARIZATION;

	 hTransform = cmsCreateMultiprofileTransform(Profiles, nargs, 0, 0, Intent, dwFlags);
	 if (hTransform) {

        size_t size = sizeof(int) + nargs * sizeof(cmsPSEQDESC);
        LPcmsSEQ pseq = (LPcmsSEQ) _cmsMalloc(size);
        
        ZeroMemory(pseq, size);
        pseq ->n = nargs;

        for (i=0; i < nargs; i++) {

            strcpy(pseq ->seq[i].Manufacturer, cmsTakeManufacturer(Profiles[i]));
            strcpy(pseq ->seq[1].Model, cmsTakeModel(Profiles[i]));
        }
	       
		hProfile = 	cmsTransform2DeviceLink(hTransform, dwFlags);

		cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) Description);
		cmsAddTag(hProfile, icSigCopyrightTag, (LPVOID) "Generated by littlecms icclink. No copyright, use freely");
        cmsAddTag(hProfile, icSigProfileSequenceDescTag, (LPVOID) pseq);

        if (lUse8bits) _cmsSetLUTdepth(hProfile, 8);

		if (_cmsSaveProfile(hProfile, cOutProf)) 
				fprintf(stderr, "Ok");
		else 
				fprintf(stderr, "Error saving file!");

		cmsCloseProfile(hProfile);
        _cmsFree(pseq);
	 }

	 cmsDeleteTransform(hTransform);

	 for (i=0; i < nargs; i++) {
		 cmsCloseProfile(Profiles[i]);
	 }

		 	
     return 0;     
}
Example #8
0
static
void OpenTransforms(int argc, char *argv[])
{
    
    DWORD dwIn, dwOut, dwFlags;
    

	if (lMultiProfileChain) {

		int i;
		cmsHTRANSFORM hTmp;

		
		nProfiles = argc - xoptind;
		for (i=0; i < nProfiles; i++) {

			hProfiles[i] = OpenProfile(argv[i+xoptind]);
		}

	
		// Create a temporary devicelink 

		hTmp = cmsCreateMultiprofileTransform(hProfiles, nProfiles, 
							0, 0, Intent, GetFlags());

		hInput = cmsTransform2DeviceLink(hTmp, 0);
		hOutput = NULL;
		cmsDeleteTransform(hTmp);

		InputColorSpace  = cmsGetColorSpace(hInput);
        OutputColorSpace = cmsGetPCS(hInput);        
		lIsDeviceLink = TRUE;

	}
	else
    if (lIsDeviceLink) {
        
        hInput  = cmsOpenProfileFromFile(cInProf, "r");
        hOutput = NULL;
        InputColorSpace  = cmsGetColorSpace(hInput);
        OutputColorSpace = cmsGetPCS(hInput);
        
        
    }
    else {
        
        hInput  = OpenProfile(cInProf);
        hOutput = OpenProfile(cOutProf);    
        
        InputColorSpace   = cmsGetColorSpace(hInput);
        OutputColorSpace  = cmsGetColorSpace(hOutput);
        
        if (cmsGetDeviceClass(hInput) == icSigLinkClass ||
            cmsGetDeviceClass(hOutput) == icSigLinkClass)   
            FatalError("Use %cl flag for devicelink profiles!\n", SW);
    
    }
    
    
    
    if (Verbose) {
        
        mexPrintf("From: %s\n", cmsTakeProductName(hInput));
        if (hOutput) mexPrintf("To  : %s\n\n", cmsTakeProductName(hOutput));
        
    }
    
        
    OutputChannels = _cmsChannelsOf(OutputColorSpace);
	InputChannels  = _cmsChannelsOf(InputColorSpace);
    

    dwIn  = MakeFormatDescriptor(InputColorSpace, nBytesDepth);
    dwOut = MakeFormatDescriptor(OutputColorSpace, nBytesDepth);
    
 
    dwFlags = GetFlags();
    
    if (cProofing != NULL) {

                   hProof = OpenProfile(cProofing);
                   dwFlags |= cmsFLAGS_SOFTPROOFING;
    }

   


     hColorTransform = cmsCreateProofingTransform(hInput, dwIn, 
                                          hOutput, dwOut, 
                                          hProof, Intent, 
                                          ProofingIntent, 
                                          dwFlags);
      
     
    
}
Example #9
0
int main(int argc, char *argv[])
{
    int i, nargs, rc;
    cmsHPROFILE Profiles[257];
    cmsHPROFILE hProfile;
    cmsUInt32Number dwFlags;
    cmsHTRANSFORM hTransform = NULL;

    // Here we are
    fprintf(stderr, "little cms ICC device link generator - v2.2 [LittleCMS %2.2f]\n", LCMS_VERSION / 1000.0);
    fflush(stderr);

    // Initialize
    InitUtils("linkicc");
    rc = 0;
    
    // Get the options
    HandleSwitches(argc, argv);

    // How many profiles to link?
    nargs = (argc - xoptind);
    if (nargs < 1)
        return Help(0); 

    if (nargs > 255) {
        FatalError("Holy profile! what are you trying to do with so many profiles!?");
        goto Cleanup;
    }

    // Open all profiles
    memset(Profiles, 0, sizeof(Profiles));
    for (i=0; i < nargs; i++) {

        Profiles[i] = OpenStockProfile(0, argv[i + xoptind]);
        if (Profiles[i] == NULL) goto Cleanup;      

        if (Verbose >= 1) {
            PrintProfileInformation(Profiles[i]);
        }
    }

    // Ink limiting
    if (InkLimit != 400.0) {        
        cmsColorSpaceSignature EndingColorSpace = cmsGetColorSpace(Profiles[nargs-1]);
        Profiles[nargs++] = cmsCreateInkLimitingDeviceLink(EndingColorSpace, InkLimit);
    }

    // Set the flags
    dwFlags = cmsFLAGS_KEEP_SEQUENCE;
    switch (PrecalcMode) {

        case 0: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
        case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
        case 1: 
            if (NumOfGridPoints > 0)
                dwFlags |= cmsFLAGS_GRIDPOINTS(NumOfGridPoints);
            break;

        default: 
            {
                FatalError("Unknown precalculation mode '%d'", PrecalcMode);
                goto Cleanup;
            }
    }

    if (BlackPointCompensation)
        dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;

    if (TagResult)
        dwFlags |= cmsFLAGS_GUESSDEVICECLASS;

    if (KeepLinearization)
        dwFlags |= cmsFLAGS_CLUT_PRE_LINEARIZATION|cmsFLAGS_CLUT_POST_LINEARIZATION;

    if (lUse8bits) dwFlags |= cmsFLAGS_8BITS_DEVICELINK;

     cmsSetAdaptationState(ObserverAdaptationState);
     
    // Create the color transform. Specify 0 for the format is safe as the transform 
    // is intended to be used only for the devicelink.
    hTransform = cmsCreateMultiprofileTransform(Profiles, nargs, 0, 0, Intent, dwFlags|cmsFLAGS_NOOPTIMIZE);
    if (hTransform == NULL) {
        FatalError("Transform creation failed");
        goto Cleanup;
    }

    hProfile =  cmsTransform2DeviceLink(hTransform, Version, dwFlags);
    if (hProfile == NULL) {
        FatalError("Devicelink creation failed");
        goto Cleanup;
    }

    SetTextTags(hProfile);
    cmsSetHeaderRenderingIntent(hProfile, Intent);

    if (cmsSaveProfileToFile(hProfile, cOutProf)) {

        if (Verbose > 0) 
            fprintf(stderr, "Ok");
    }
    else 
        FatalError("Error saving file!");

    cmsCloseProfile(hProfile);


Cleanup:

    if (hTransform != NULL) cmsDeleteTransform(hTransform);
    for (i=0; i < nargs; i++) {

        if (Profiles[i] != NULL) cmsCloseProfile(Profiles[i]);
    }

    return rc;     
}