// This function does create the virtual output profile, with the necessary gamut mapping static cmsHPROFILE CreatePCS2ITU_ICC(void) { cmsHPROFILE hProfile; cmsPipeline* BToA0; cmsStage* ColorMap; BToA0 = cmsPipelineAlloc(0, 3, 3); if (BToA0 == NULL) return NULL; ColorMap = cmsStageAllocCLut16bit(0, GRID_POINTS, 3, 3, NULL); if (ColorMap == NULL) return NULL; cmsPipelineInsertStage(BToA0, cmsAT_BEGIN, ColorMap); cmsStageSampleCLut16bit(ColorMap, PCS2ITU, NULL, 0); hProfile = cmsCreateProfilePlaceholder(0); if (hProfile == NULL) { cmsPipelineFree(BToA0); return NULL; } cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA0); cmsSetColorSpace(hProfile, cmsSigLabData); cmsSetPCS(hProfile, cmsSigLabData); cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass); cmsPipelineFree(BToA0); return hProfile; }
// Create the ICC virtual profile for adobe rgb space cmsHPROFILE dt_colorspaces_create_adobergb_profile(void) { cmsHPROFILE hAdobeRGB; cmsCIEXYZTRIPLE Colorants = { {0.609741, 0.311111, 0.019470}, {0.205276, 0.625671, 0.060867}, {0.149185, 0.063217, 0.744568} }; cmsCIEXYZ black = { 0, 0, 0 }; cmsCIEXYZ D65 = { 0.95045, 1, 1.08905 }; cmsToneCurve* transferFunction; transferFunction = cmsBuildGamma(NULL, 2.2); hAdobeRGB = cmsCreateProfilePlaceholder(0); cmsSetProfileVersion(hAdobeRGB, 2.1); cmsMLU *mlu0 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu0, "en", "US", "Public Domain"); cmsMLU *mlu1 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu1, "en", "US", "AdobeRGB"); cmsMLU *mlu2 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu2, "en", "US", "Darktable"); cmsMLU *mlu3 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu3, "en", "US", "AdobeRGB"); // this will only be displayed when the embedded profile is read by for example GIMP cmsWriteTag(hAdobeRGB, cmsSigCopyrightTag, mlu0); cmsWriteTag(hAdobeRGB, cmsSigProfileDescriptionTag, mlu1); cmsWriteTag(hAdobeRGB, cmsSigDeviceMfgDescTag, mlu2); cmsWriteTag(hAdobeRGB, cmsSigDeviceModelDescTag, mlu3); cmsMLUfree(mlu0); cmsMLUfree(mlu1); cmsMLUfree(mlu2); cmsMLUfree(mlu3); cmsSetDeviceClass(hAdobeRGB, cmsSigDisplayClass); cmsSetColorSpace(hAdobeRGB, cmsSigRgbData); cmsSetPCS(hAdobeRGB, cmsSigXYZData); cmsWriteTag(hAdobeRGB, cmsSigMediaWhitePointTag, &D65); cmsWriteTag(hAdobeRGB, cmsSigMediaBlackPointTag, &black); cmsWriteTag(hAdobeRGB, cmsSigRedColorantTag, (void*) &Colorants.Red); cmsWriteTag(hAdobeRGB, cmsSigGreenColorantTag, (void*) &Colorants.Green); cmsWriteTag(hAdobeRGB, cmsSigBlueColorantTag, (void*) &Colorants.Blue); cmsWriteTag(hAdobeRGB, cmsSigRedTRCTag, (void*) transferFunction); cmsLinkTag(hAdobeRGB, cmsSigGreenTRCTag, cmsSigRedTRCTag ); cmsLinkTag(hAdobeRGB, cmsSigBlueTRCTag, cmsSigRedTRCTag ); return hAdobeRGB; }
cmsHPROFILE dt_colorspaces_create_srgb_profile() { cmsHPROFILE hsRGB; cmsCIEXYZTRIPLE Colorants = { {0.436066, 0.222488, 0.013916}, {0.385147, 0.716873, 0.097076}, {0.143066, 0.060608, 0.714096} }; cmsCIEXYZ black = { 0, 0, 0 }; cmsCIEXYZ D65 = { 0.95045, 1, 1.08905 }; cmsToneCurve* transferFunction; transferFunction = cmsBuildTabulatedToneCurve16(NULL, dt_srgb_tone_curve_values_n, dt_srgb_tone_curve_values); hsRGB = cmsCreateProfilePlaceholder(0); cmsSetProfileVersion(hsRGB, 2.1); cmsMLU *mlu0 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu0, "en", "US", "Public Domain"); cmsMLU *mlu1 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu1, "en", "US", "sRGB"); cmsMLU *mlu2 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu2, "en", "US", "Darktable"); cmsMLU *mlu3 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu3, "en", "US", "sRGB"); // this will only be displayed when the embedded profile is read by for example GIMP cmsWriteTag(hsRGB, cmsSigCopyrightTag, mlu0); cmsWriteTag(hsRGB, cmsSigProfileDescriptionTag, mlu1); cmsWriteTag(hsRGB, cmsSigDeviceMfgDescTag, mlu2); cmsWriteTag(hsRGB, cmsSigDeviceModelDescTag, mlu3); cmsMLUfree(mlu0); cmsMLUfree(mlu1); cmsMLUfree(mlu2); cmsMLUfree(mlu3); cmsSetDeviceClass(hsRGB, cmsSigDisplayClass); cmsSetColorSpace(hsRGB, cmsSigRgbData); cmsSetPCS(hsRGB, cmsSigXYZData); cmsWriteTag(hsRGB, cmsSigMediaWhitePointTag, &D65); cmsWriteTag(hsRGB, cmsSigMediaBlackPointTag, &black); cmsWriteTag(hsRGB, cmsSigRedColorantTag, (void*) &Colorants.Red); cmsWriteTag(hsRGB, cmsSigGreenColorantTag, (void*) &Colorants.Green); cmsWriteTag(hsRGB, cmsSigBlueColorantTag, (void*) &Colorants.Blue); cmsWriteTag(hsRGB, cmsSigRedTRCTag, (void*) transferFunction); cmsLinkTag(hsRGB, cmsSigGreenTRCTag, cmsSigRedTRCTag ); cmsLinkTag(hsRGB, cmsSigBlueTRCTag, cmsSigRedTRCTag ); return hsRGB; }
cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, cmsColorSpaceSignature ColorSpace, cmsToneCurve* const TransferFunctions[]) { cmsHPROFILE hICC; cmsPipeline* Pipeline; cmsStage* Lin; int nChannels; hICC = cmsCreateProfilePlaceholder(ContextID); if (!hICC) return NULL; cmsSetProfileVersion(hICC, 4.3); cmsSetDeviceClass(hICC, cmsSigLinkClass); cmsSetColorSpace(hICC, ColorSpace); cmsSetPCS(hICC, ColorSpace); cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); // Set up channels nChannels = cmsChannelsOf(ColorSpace); // Creates a Pipeline with prelinearization step only Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels); if (Pipeline == NULL) goto Error; // Copy tables to Pipeline Lin = cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions); if (Lin == NULL) goto Error; cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, Lin); // Create tags if (!SetTextTags(hICC, L"Linearization built-in")) goto Error; if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error; if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error; // Pipeline is already on virtual profile cmsPipelineFree(Pipeline); // Ok, done return hICC; Error: if (hICC) cmsCloseProfile(hICC); return NULL; }
// 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; }
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; }
static GimpColorProfile * gimp_color_profile_new_from_color_profile (GimpColorProfile *profile, gboolean linear) { GimpColorProfile *new_profile; cmsHPROFILE target_profile; GimpMatrix3 matrix = { 0, }; cmsCIEXYZ *whitepoint; cmsToneCurve *curve; const gchar *model; gchar *new_model; g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL); if (gimp_color_profile_is_rgb (profile)) { if (! gimp_color_profile_get_rgb_matrix_colorants (profile, &matrix)) return NULL; } else if (! gimp_color_profile_is_gray (profile)) { return NULL; } whitepoint = cmsReadTag (profile->priv->lcms_profile, cmsSigMediaWhitePointTag); target_profile = cmsCreateProfilePlaceholder (0); cmsSetProfileVersion (target_profile, 4.3); cmsSetDeviceClass (target_profile, cmsSigDisplayClass); cmsSetPCS (target_profile, cmsSigXYZData); cmsWriteTag (target_profile, cmsSigMediaWhitePointTag, whitepoint); if (linear) { /* linear light */ curve = cmsBuildGamma (NULL, 1.00); gimp_color_profile_set_tag (target_profile, cmsSigProfileDescriptionTag, "linear TRC variant generated by GIMP"); } else { cmsFloat64Number srgb_parameters[5] = { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 }; /* sRGB curve */ curve = cmsBuildParametricToneCurve (NULL, 4, srgb_parameters); gimp_color_profile_set_tag (target_profile, cmsSigProfileDescriptionTag, "sRGB TRC variant generated by GIMP"); } if (gimp_color_profile_is_rgb (profile)) { cmsCIEXYZ red; cmsCIEXYZ green; cmsCIEXYZ blue; cmsSetColorSpace (target_profile, cmsSigRgbData); red.X = matrix.coeff[0][0]; red.Y = matrix.coeff[0][1]; red.Z = matrix.coeff[0][2]; green.X = matrix.coeff[1][0]; green.Y = matrix.coeff[1][1]; green.Z = matrix.coeff[1][2]; blue.X = matrix.coeff[2][0]; blue.Y = matrix.coeff[2][1]; blue.Z = matrix.coeff[2][2]; cmsWriteTag (target_profile, cmsSigRedColorantTag, &red); cmsWriteTag (target_profile, cmsSigGreenColorantTag, &green); cmsWriteTag (target_profile, cmsSigBlueColorantTag, &blue); cmsWriteTag (target_profile, cmsSigRedTRCTag, curve); cmsWriteTag (target_profile, cmsSigGreenTRCTag, curve); cmsWriteTag (target_profile, cmsSigBlueTRCTag, curve); } else { cmsSetColorSpace (target_profile, cmsSigGrayData); cmsWriteTag (target_profile, cmsSigGrayTRCTag, curve); } cmsFreeToneCurve (curve); model = gimp_color_profile_get_model (profile); if (model && g_str_has_prefix (model, "Generated from '")) { /* don't add multiple "Generated from 'foo'" */ new_model = g_strdup (model); } else { new_model = g_strdup_printf ("Generated from '%s'", gimp_color_profile_get_description (profile)); } gimp_color_profile_set_tag (target_profile, cmsSigDeviceMfgDescTag, "GIMP"); gimp_color_profile_set_tag (target_profile, cmsSigDeviceModelDescTag, new_model); gimp_color_profile_set_tag (target_profile, cmsSigCopyrightTag, "Public Domain"); g_free (new_model); new_profile = gimp_color_profile_new_from_lcms_profile (target_profile, NULL); cmsCloseProfile (target_profile); return new_profile; }