// This function creates a profile based on White point and transfer function. cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint, const cmsToneCurve* TransferFunction) { cmsHPROFILE hICC; cmsCIEXYZ tmp; hICC = cmsCreateProfilePlaceholder(ContextID); if (!hICC) // can't allocate return NULL; cmsSetProfileVersion(hICC, 4.3); cmsSetDeviceClass(hICC, cmsSigDisplayClass); cmsSetColorSpace(hICC, cmsSigGrayData); cmsSetPCS(hICC, cmsSigXYZData); cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); // Implement profile using following tags: // // 1 cmsSigProfileDescriptionTag // 2 cmsSigMediaWhitePointTag // 3 cmsSigGrayTRCTag // This conforms a standard Gray DisplayProfile // Fill-in the tags if (!SetTextTags(hICC, L"gray built-in")) goto Error; if (WhitePoint) { cmsxyY2XYZ(&tmp, WhitePoint); if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error; } if (TransferFunction) { if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error; } return hICC; Error: if (hICC) cmsCloseProfile(hICC); return NULL; }
// This function creates a named color profile dumping all the contents of transform to a single profile // In this way, LittleCMS may be used to "group" several named color databases into a single profile. // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this // is the normal PCS for named color profiles. static cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) { _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; cmsHPROFILE hICC = NULL; int i, nColors; cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL; // Create an empty placeholder hICC = cmsCreateProfilePlaceholder(v->ContextID); if (hICC == NULL) return NULL; // Critical information cmsSetDeviceClass(hICC, cmsSigNamedColorClass); cmsSetColorSpace(hICC, v ->ExitColorSpace); cmsSetPCS(hICC, cmsSigLabData); // Tag profile with information if (!SetTextTags(hICC, L"Named color devicelink")) goto Error; Original = cmsGetNamedColorList(xform); if (Original == NULL) goto Error; nColors = cmsNamedColorCount(Original); nc2 = cmsDupNamedColorList(Original); if (nc2 == NULL) goto Error; // Colorant count now depends on the output space nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut); // Make sure we have proper formatters cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX, FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace)) | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace))); // Apply the transfor to colorants. for (i=0; i < nColors; i++) { cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); } if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error; cmsFreeNamedColorList(nc2); return hICC; Error: if (hICC != NULL) cmsCloseProfile(hICC); return NULL; }
// Creates a fake NULL profile. This profile return 1 channel as always 0. // Is useful only for gamut checking tricks cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID) { cmsHPROFILE hProfile; cmsPipeline* LUT = NULL; cmsStage* PostLin; cmsToneCurve* EmptyTab; cmsUInt16Number Zero[2] = { 0, 0 }; hProfile = cmsCreateProfilePlaceholder(ContextID); if (!hProfile) // can't allocate return NULL; cmsSetProfileVersion(hProfile, 4.3); if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error; cmsSetDeviceClass(hProfile, cmsSigOutputClass); cmsSetColorSpace(hProfile, cmsSigGrayData); cmsSetPCS(hProfile, cmsSigLabData); // An empty LUTs is all we need LUT = cmsPipelineAlloc(ContextID, 1, 1); if (LUT == NULL) goto Error; EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); PostLin = cmsStageAllocToneCurves(ContextID, 1, &EmptyTab); cmsFreeToneCurve(EmptyTab); if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin)) goto Error; if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error; if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; cmsPipelineFree(LUT); return hProfile; Error: if (LUT != NULL) cmsPipelineFree(LUT); if (hProfile != NULL) cmsCloseProfile(hProfile); return NULL; }
// Creates a fake XYZ identity cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID) { cmsHPROFILE hProfile; cmsPipeline* LUT = NULL; hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL); if (hProfile == NULL) return NULL; cmsSetProfileVersion(hProfile, 4.3); cmsSetDeviceClass(hProfile, cmsSigAbstractClass); cmsSetColorSpace(hProfile, cmsSigXYZData); cmsSetPCS(hProfile, cmsSigXYZData); if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error; // An identity LUT is all we need LUT = cmsPipelineAlloc(ContextID, 3, 3); if (LUT == NULL) goto Error; if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3))) goto Error; if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; cmsPipelineFree(LUT); return hProfile; Error: if (LUT != NULL) cmsPipelineFree(LUT); if (hProfile != NULL) cmsCloseProfile(hProfile); return NULL; }
// Creates a fake Lab identity. cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint) { cmsHPROFILE hProfile; cmsPipeline* LUT = NULL; hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); if (hProfile == NULL) return NULL; cmsSetProfileVersion(hProfile, 2.1); cmsSetDeviceClass(hProfile, cmsSigAbstractClass); cmsSetColorSpace(hProfile, cmsSigLabData); cmsSetPCS(hProfile, cmsSigLabData); if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL; // An identity LUT is all we need LUT = cmsPipelineAlloc(ContextID, 3, 3); if (LUT == NULL) goto Error; if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3))) goto Error; if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; cmsPipelineFree(LUT); return hProfile; Error: if (LUT != NULL) cmsPipelineFree(LUT); if (hProfile != NULL) cmsCloseProfile(hProfile); return NULL; }
cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, int nLUTPoints, cmsFloat64Number Bright, cmsFloat64Number Contrast, cmsFloat64Number Hue, cmsFloat64Number Saturation, int TempSrc, int TempDest) { cmsHPROFILE hICC; cmsPipeline* Pipeline; BCHSWADJUSTS bchsw; cmsCIExyY WhitePnt; cmsStage* CLUT; cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; int i; bchsw.Brightness = Bright; bchsw.Contrast = Contrast; bchsw.Hue = Hue; bchsw.Saturation = Saturation; cmsWhitePointFromTemp(&WhitePnt, TempSrc ); cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt); cmsWhitePointFromTemp(&WhitePnt, TempDest); cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt); hICC = cmsCreateProfilePlaceholder(ContextID); if (!hICC) // can't allocate return NULL; cmsSetDeviceClass(hICC, cmsSigAbstractClass); cmsSetColorSpace(hICC, cmsSigLabData); cmsSetPCS(hICC, cmsSigLabData); cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); // Creates a Pipeline with 3D grid only Pipeline = cmsPipelineAlloc(ContextID, 3, 3); if (Pipeline == NULL) { cmsCloseProfile(hICC); return NULL; } for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints; CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL); if (CLUT == NULL) return NULL; if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) { // Shouldn't reach here goto Error; } if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) { goto Error; } // Create tags if (!SetTextTags(hICC, L"BCHS built-in")) return NULL; cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ()); cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline); // Pipeline is already on virtual profile cmsPipelineFree(Pipeline); // Ok, done return hICC; Error: cmsPipelineFree(Pipeline); cmsCloseProfile(hICC); return NULL; }
cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit) { cmsHPROFILE hICC; cmsPipeline* LUT; cmsStage* CLUT; int nChannels; if (ColorSpace != cmsSigCmykData) { cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported"); return NULL; } if (Limit < 0.0 || Limit > 400) { cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400"); if (Limit < 0) Limit = 0; if (Limit > 400) Limit = 400; } hICC = cmsCreateProfilePlaceholder(ContextID); if (!hICC) // can't allocate return NULL; cmsSetProfileVersion(hICC, 4.3); cmsSetDeviceClass(hICC, cmsSigLinkClass); cmsSetColorSpace(hICC, ColorSpace); cmsSetPCS(hICC, ColorSpace); cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); // Creates a Pipeline with 3D grid only LUT = cmsPipelineAlloc(ContextID, 4, 4); if (LUT == NULL) goto Error; nChannels = cmsChannelsOf(ColorSpace); CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL); if (CLUT == NULL) goto Error; if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error; if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) || !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) || !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels))) goto Error; // Create tags if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error; if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error; if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error; // cmsPipeline is already on virtual profile cmsPipelineFree(LUT); // Ok, done return hICC; Error: if (LUT != NULL) cmsPipelineFree(LUT); if (hICC != NULL) cmsCloseProfile(hICC); return NULL; }
// Does convert a transform into a device link profile cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags) { cmsHPROFILE hProfile = NULL; cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut; cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut; _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; cmsPipeline* LUT = NULL; cmsStage* mpe; cmsContext ContextID = cmsGetTransformContextID(hTransform); const cmsAllowedLUT* AllowedLUT; cmsTagSignature DestinationTag; cmsProfileClassSignature deviceClass; _cmsAssert(hTransform != NULL); // Get the first mpe to check for named color mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut); // Check if is a named color transform if (mpe != NULL) { if (cmsStageType(mpe) == cmsSigNamedColorElemType) { return CreateNamedColorDevicelink(hTransform); } } // First thing to do is to get a copy of the transformation LUT = cmsPipelineDup(xform ->Lut); if (LUT == NULL) return NULL; // Time to fix the Lab2/Lab4 issue. if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) { if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID))) goto Error; } // On the output side too if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) { if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID))) goto Error; } hProfile = cmsCreateProfilePlaceholder(ContextID); if (!hProfile) goto Error; // can't allocate cmsSetProfileVersion(hProfile, Version); FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags); // Optimize the LUT and precalculate a devicelink ChansIn = cmsChannelsOf(xform -> EntryColorSpace); ChansOut = cmsChannelsOf(xform -> ExitColorSpace); ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace); ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace); FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2); FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2); deviceClass = cmsGetDeviceClass(hProfile); if (deviceClass == cmsSigOutputClass) DestinationTag = cmsSigBToA0Tag; else DestinationTag = cmsSigAToB0Tag; // Check if the profile/version can store the result if (dwFlags & cmsFLAGS_FORCE_CLUT) AllowedLUT = NULL; else AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); if (AllowedLUT == NULL) { // Try to optimize _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); } // If no way, then force CLUT that for sure can be written if (AllowedLUT == NULL) { dwFlags |= cmsFLAGS_FORCE_CLUT; _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); // Put identity curves if needed if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType) if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn))) goto Error; if (cmsPipelineGetPtrToLastStage(LUT) ->Type != cmsSigCurveSetElemType) if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut))) goto Error; AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); } // Somethings is wrong... if (AllowedLUT == NULL) { goto Error; } if (dwFlags & cmsFLAGS_8BITS_DEVICELINK) cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE); // Tag profile with information if (!SetTextTags(hProfile, L"devicelink")) goto Error; // Store result if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error; if (xform -> InputColorant != NULL) { if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error; } if (xform -> OutputColorant != NULL) { if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error; } if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) { if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error; } // Set the white point if (deviceClass == cmsSigInputClass) { if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error; } else { if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error; } // Per 7.2.15 in spec 4.3 cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent); cmsPipelineFree(LUT); return hProfile; Error: if (LUT != NULL) cmsPipelineFree(LUT); cmsCloseProfile(hProfile); return NULL; }
// This function creates a profile based on White point, primaries and // transfer functions. cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries, cmsToneCurve* const TransferFunction[3]) { cmsHPROFILE hICC; cmsMAT3 MColorants; cmsCIEXYZTRIPLE Colorants; cmsCIExyY MaxWhite; cmsMAT3 CHAD; cmsCIEXYZ WhitePointXYZ; hICC = cmsCreateProfilePlaceholder(ContextID); if (!hICC) // can't allocate return NULL; cmsSetProfileVersion(hICC, 4.3); cmsSetDeviceClass(hICC, cmsSigDisplayClass); cmsSetColorSpace(hICC, cmsSigRgbData); cmsSetPCS(hICC, cmsSigXYZData); cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); // Implement profile using following tags: // // 1 cmsSigProfileDescriptionTag // 2 cmsSigMediaWhitePointTag // 3 cmsSigRedColorantTag // 4 cmsSigGreenColorantTag // 5 cmsSigBlueColorantTag // 6 cmsSigRedTRCTag // 7 cmsSigGreenTRCTag // 8 cmsSigBlueTRCTag // 9 Chromatic adaptation Tag // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II) // 10 cmsSigChromaticityTag if (!SetTextTags(hICC, L"RGB built-in")) goto Error; if (WhitePoint) { if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; cmsxyY2XYZ(&WhitePointXYZ, WhitePoint); _cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ()); // This is a V4 tag, but many CMM does read and understand it no matter which version if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error; } if (WhitePoint && Primaries) { MaxWhite.x = WhitePoint -> x; MaxWhite.y = WhitePoint -> y; MaxWhite.Y = 1.0; if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error; Colorants.Red.X = MColorants.v[0].n[0]; Colorants.Red.Y = MColorants.v[1].n[0]; Colorants.Red.Z = MColorants.v[2].n[0]; Colorants.Green.X = MColorants.v[0].n[1]; Colorants.Green.Y = MColorants.v[1].n[1]; Colorants.Green.Z = MColorants.v[2].n[1]; Colorants.Blue.X = MColorants.v[0].n[2]; Colorants.Blue.Y = MColorants.v[1].n[2]; Colorants.Blue.Z = MColorants.v[2].n[2]; if (!cmsWriteTag(hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error; if (!cmsWriteTag(hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error; if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error; } if (TransferFunction) { // Tries to minimize space. Thanks to Richard Hughes for this nice idea if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error; if (TransferFunction[1] == TransferFunction[0]) { if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error; } else { if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error; } if (TransferFunction[2] == TransferFunction[0]) { if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error; } else { if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error; } } if (Primaries) { if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error; } return hICC; Error: if (hICC) cmsCloseProfile(hICC); return NULL; }
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; }