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; }
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; if (TempSrc == TempDest) { bchsw.lAdjustWP = FALSE; } else { bchsw.lAdjustWP = TRUE; 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; }
// 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; }
bool ProfileUtils::createIccProfile(bool isLaptop, const Edid &edid, const QString &filename) { cmsCIExyYTRIPLE chroma; cmsCIExyY white_point; cmsHPROFILE lcms_profile = NULL; cmsToneCurve *transfer_curve[3] = { NULL, NULL, NULL }; bool ret = false; cmsHANDLE dict = NULL; /* ensure the per-user directory exists */ // Create dir path if not available // check if the file doesn't already exist QFileInfo fileInfo(filename); if (fileInfo.exists()) { qCWarning(COLORD) << "EDID ICC Profile already exists" << filename; if (*transfer_curve != NULL) cmsFreeToneCurve(*transfer_curve); return false; } // copy color data from our structures // Red chroma.Red.x = edid.red().x(); chroma.Red.y = edid.red().y(); // Green chroma.Green.x = edid.green().x(); chroma.Green.y = edid.green().y(); // Blue chroma.Blue.x = edid.blue().x(); chroma.Blue.y = edid.blue().y(); // White white_point.x = edid.white().x(); white_point.y = edid.white().y(); white_point.Y = 1.0; // estimate the transfer function for the gamma transfer_curve[0] = transfer_curve[1] = transfer_curve[2] = cmsBuildGamma(NULL, edid.gamma()); // create our generated profile lcms_profile = cmsCreateRGBProfile(&white_point, &chroma, transfer_curve); if (lcms_profile == NULL) { qCWarning(COLORD) << "Failed to create ICC profile on cmsCreateRGBProfile"; if (*transfer_curve != NULL) cmsFreeToneCurve(*transfer_curve); return false; } cmsSetColorSpace(lcms_profile, cmsSigRgbData); cmsSetPCS(lcms_profile, cmsSigXYZData); cmsSetHeaderRenderingIntent(lcms_profile, INTENT_RELATIVE_COLORIMETRIC); cmsSetDeviceClass(lcms_profile, cmsSigDisplayClass); // copyright ret = cmsWriteTagTextAscii(lcms_profile, cmsSigCopyrightTag, "No copyright"); if (!ret) { qCWarning(COLORD) << "Failed to write copyright"; if (*transfer_curve != NULL) cmsFreeToneCurve(*transfer_curve); return false; } // set model QString model; if (isLaptop) { model = DmiUtils::deviceModel(); } else { model = edid.name(); } if (model.isEmpty()) { model = QStringLiteral("Unknown monitor"); } ret = cmsWriteTagTextAscii(lcms_profile, cmsSigDeviceModelDescTag, model); if (!ret) { qCWarning(COLORD) << "Failed to write model"; if (*transfer_curve != NULL) { cmsFreeToneCurve(*transfer_curve); } return false; } // write title ret = cmsWriteTagTextAscii(lcms_profile, cmsSigProfileDescriptionTag, model); if (!ret) { qCWarning(COLORD) << "Failed to write description"; if (*transfer_curve != NULL) cmsFreeToneCurve(*transfer_curve); return false; } // get manufacturer QString vendor; if (isLaptop) { vendor = DmiUtils::deviceVendor(); } else { vendor = edid.vendor(); } if (vendor.isEmpty()) { vendor = QStringLiteral("Unknown vendor"); } ret = cmsWriteTagTextAscii(lcms_profile, cmsSigDeviceMfgDescTag, vendor); if (!ret) { qCWarning(COLORD) << "Failed to write manufacturer"; if (*transfer_curve != NULL) cmsFreeToneCurve(*transfer_curve); return false; } // just create a new dict dict = cmsDictAlloc(NULL); // set the framework creator metadata cmsDictAddEntryAscii(dict, CD_PROFILE_METADATA_CMF_PRODUCT, PACKAGE_NAME); cmsDictAddEntryAscii(dict, CD_PROFILE_METADATA_CMF_BINARY, PACKAGE_NAME); cmsDictAddEntryAscii(dict, CD_PROFILE_METADATA_CMF_VERSION, PACKAGE_VERSION); /* set the data source so we don't ever prompt the user to * recalibrate (as the EDID data won't have changed) */ cmsDictAddEntryAscii(dict, CD_PROFILE_METADATA_DATA_SOURCE, CD_PROFILE_METADATA_DATA_SOURCE_EDID); // set 'ICC meta Tag for Monitor Profiles' data cmsDictAddEntryAscii(dict, "EDID_md5", edid.hash()); if (!model.isEmpty()) cmsDictAddEntryAscii(dict, "EDID_model", model); if (!edid.serial().isEmpty()) { cmsDictAddEntryAscii(dict, "EDID_serial", edid.serial()); } if (!edid.pnpId().isEmpty()) { cmsDictAddEntryAscii(dict, "EDID_mnft", edid.pnpId()); } if (!vendor.isEmpty()) { cmsDictAddEntryAscii(dict, "EDID_manufacturer", vendor); } /* write new tag */ ret = cmsWriteTag(lcms_profile, cmsSigMetaTag, dict); if (!ret) { qCWarning(COLORD) << "Failed to write profile metadata"; if (*transfer_curve != NULL) cmsFreeToneCurve(*transfer_curve); return false; } /* write profile id */ ret = cmsMD5computeID(lcms_profile); if (!ret) { qCWarning(COLORD) << "Failed to write profile id"; if (dict != NULL) cmsDictFree (dict); if (*transfer_curve != NULL) cmsFreeToneCurve(*transfer_curve); return false; } /* save, TODO: get error */ ret = cmsSaveProfileToFile(lcms_profile, filename.toUtf8()); if (dict != NULL) { cmsDictFree (dict); } if (*transfer_curve != NULL) { cmsFreeToneCurve (*transfer_curve); } return ret; }
cmsHPROFILE LCMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints, double Bright, double Contrast, double Hue, double Saturation, int TempSrc, int TempDest) { cmsHPROFILE hICC; LPLUT Lut; BCHSWADJUSTS bchsw; cmsCIExyY WhitePnt; bchsw.Brightness = Bright; bchsw.Contrast = Contrast; bchsw.Hue = Hue; bchsw.Saturation = Saturation; cmsWhitePointFromTemp(TempSrc, &WhitePnt); cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt); cmsWhitePointFromTemp(TempDest, &WhitePnt); cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt); hICC = _cmsCreateProfilePlaceholder(); if (!hICC) // can't allocate return NULL; cmsSetDeviceClass(hICC, icSigAbstractClass); cmsSetColorSpace(hICC, icSigLabData); cmsSetPCS(hICC, icSigLabData); cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); // Creates a LUT with 3D grid only Lut = cmsAllocLUT(); if (Lut == NULL) { cmsCloseProfile(hICC); return NULL; } cmsAlloc3DGrid(Lut, nLUTPoints, 3, 3); if (!cmsSample3DGrid(Lut, bchswSampler, (LPVOID) &bchsw, 0)) { // Shouldn't reach here cmsFreeLUT(Lut); cmsCloseProfile(hICC); return NULL; } // Create tags cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms BCHSW abstract profile"); cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "BCHSW built-in"); cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); // LUT is already on virtual profile cmsFreeLUT(Lut); // Ok, done return hICC; }
cmsHPROFILE LCMSEXPORT cmsCreateInkLimitingDeviceLink(icColorSpaceSignature ColorSpace, double Limit) { cmsHPROFILE hICC; LPLUT Lut; if (ColorSpace != icSigCmykData) { cmsSignalError(LCMS_ERRC_ABORTED, "InkLimiting: Only CMYK currently supported"); return NULL; } if (Limit < 0.0 || Limit > 400) { cmsSignalError(LCMS_ERRC_WARNING, "InkLimiting: Limit should be between 0..400"); if (Limit < 0) Limit = 0; if (Limit > 400) Limit = 400; } hICC = _cmsCreateProfilePlaceholder(); if (!hICC) // can't allocate return NULL; cmsSetDeviceClass(hICC, icSigLinkClass); cmsSetColorSpace(hICC, ColorSpace); cmsSetPCS(hICC, ColorSpace); cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); // Creates a LUT with 3D grid only Lut = cmsAllocLUT(); if (Lut == NULL) { cmsCloseProfile(hICC); return NULL; } cmsAlloc3DGrid(Lut, 17, _cmsChannelsOf(ColorSpace), _cmsChannelsOf(ColorSpace)); if (!cmsSample3DGrid(Lut, InkLimitingSampler, (LPVOID) &Limit, 0)) { // Shouldn't reach here cmsFreeLUT(Lut); cmsCloseProfile(hICC); return NULL; } // Create tags cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms ink limiting device link"); cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "ink limiting built-in"); cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); // LUT is already on virtual profile cmsFreeLUT(Lut); // Ok, done return hICC; }
cmsHPROFILE LCMSEXPORT cmsCreateRGBProfile(LPcmsCIExyY WhitePoint, LPcmsCIExyYTRIPLE Primaries, LPGAMMATABLE TransferFunction[3]) { cmsHPROFILE hICC; cmsCIEXYZ tmp; MAT3 MColorants; cmsCIEXYZTRIPLE Colorants; cmsCIExyY MaxWhite; hICC = _cmsCreateProfilePlaceholder(); if (!hICC) // can't allocate return NULL; cmsSetDeviceClass(hICC, icSigDisplayClass); cmsSetColorSpace(hICC, icSigRgbData); cmsSetPCS(hICC, icSigXYZData); cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); // Implement profile using following tags: // // 1 icSigProfileDescriptionTag // 2 icSigMediaWhitePointTag // 3 icSigRedColorantTag // 4 icSigGreenColorantTag // 5 icSigBlueColorantTag // 6 icSigRedTRCTag // 7 icSigGreenTRCTag // 8 icSigBlueTRCTag // This conforms a standard RGB DisplayProfile as says ICC, and then I add // 9 icSigChromaticityTag // As addendum II // Fill-in the tags cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms RGB virtual profile"); cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "rgb built-in"); if (WhitePoint) { cmsxyY2XYZ(&tmp, WhitePoint); cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp); } if (WhitePoint && Primaries) { MaxWhite.x = WhitePoint -> x; MaxWhite.y = WhitePoint -> y; MaxWhite.Y = 1.0; if (!cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) { cmsCloseProfile(hICC); return NULL; } cmsAdaptMatrixToD50(&MColorants, &MaxWhite); 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]; cmsAddTag(hICC, icSigRedColorantTag, (LPVOID) &Colorants.Red); cmsAddTag(hICC, icSigBlueColorantTag, (LPVOID) &Colorants.Blue); cmsAddTag(hICC, icSigGreenColorantTag, (LPVOID) &Colorants.Green); } if (TransferFunction) { // In case of gamma, we must dup' the table pointer cmsAddTag(hICC, icSigRedTRCTag, (LPVOID) TransferFunction[0]); cmsAddTag(hICC, icSigGreenTRCTag, (LPVOID) TransferFunction[1]); cmsAddTag(hICC, icSigBlueTRCTag, (LPVOID) TransferFunction[2]); } if (Primaries) { cmsAddTag(hICC, icSigChromaticityTag, (LPVOID) Primaries); } return hICC; }
BOOL cmsxMonitorProfilerDo(LPMONITORPROFILERDATA sys) { cmsCIExyY White; LPLUT AToB0, BToA0; AToB0 = BToA0 = NULL; if (!*sys -> hdr.OutputProfileFile) return false; if (sys->hdr.ReferenceSheet[0] || sys->hdr.MeasurementSheet[0]) { if (sys->hdr.printf) { sys->hdr.printf("Loading sheets..."); if (sys->hdr.ReferenceSheet[0]) sys->hdr.printf("Reference sheet: %s", sys->hdr.ReferenceSheet); if (sys->hdr.MeasurementSheet[0]) sys->hdr.printf("Measurement sheet: %s", sys->hdr.MeasurementSheet); } if (!cmsxComputeMatrixShaper(sys -> hdr.ReferenceSheet, sys -> hdr.MeasurementSheet, MEDIUM_TRANSMISSIVE, sys -> Prelinearization, &sys -> hdr.WhitePoint, &sys -> hdr.BlackPoint, &sys -> hdr.Primaries)) return false; if (sys->hdr.printf) { char Buffer[1024]; _cmsIdentifyWhitePoint(Buffer, &sys ->hdr.WhitePoint); sys->hdr.printf("%s", Buffer); sys->hdr.printf("Primaries: R:%1.2g, %1.2g G:%1.2g, %1.2g B:%1.2g, %1.2g", sys->hdr.Primaries.Red.x,sys->hdr.Primaries.Red.y, sys->hdr.Primaries.Green.x, sys->hdr.Primaries.Green.y, sys->hdr.Primaries.Blue.x, sys->hdr.Primaries.Blue.y); } } CreatePrimaryMatrices(sys); cmsXYZ2xyY(&White, &sys->hdr.WhitePoint); sys->hdr.hProfile = cmsCreateRGBProfile(&White, &sys-> hdr.Primaries, sys -> Prelinearization); cmsSetDeviceClass(sys->hdr.hProfile, sys->hdr.DeviceClass); if (sys -> hdr.lUseCIECAM97s) sys->hdr.PCSType = PT_Lab; else sys->hdr.PCSType = PT_XYZ; cmsSetPCS(sys->hdr.hProfile, _cmsICCcolorSpace(sys->hdr.PCSType)); if (sys -> hdr.lUseCIECAM97s) CreateLUTS(sys, &AToB0, &BToA0); cmsxEmbedTextualInfo(&sys ->hdr); cmsAddTag(sys->hdr.hProfile, icSigMediaWhitePointTag, &sys->hdr.WhitePoint); cmsAddTag(sys->hdr.hProfile, icSigMediaBlackPointTag, &sys->hdr.BlackPoint); if (sys->hdr.ProfileVerbosityLevel >= 2) { cmsxEmbedCharTarget(&sys ->hdr); } _cmsSaveProfile(sys->hdr.hProfile, sys->hdr.OutputProfileFile); cmsCloseProfile(sys->hdr.hProfile); sys->hdr.hProfile = NULL; if (AToB0) cmsFreeLUT(AToB0); if (BToA0) cmsFreeLUT(BToA0); if (sys ->Prelinearization[0]) cmsFreeGammaTriple(sys -> Prelinearization); return true; }