cmsHPROFILE dt_colorspaces_create_xyz_profile(void) { cmsHPROFILE hXYZ = cmsCreateXYZProfile(); // revert some settings which prevent us from using XYZ as output profile: cmsSetDeviceClass(hXYZ, cmsSigDisplayClass); cmsSetColorSpace(hXYZ, cmsSigRgbData); cmsSetPCS(hXYZ, cmsSigXYZData); cmsSetHeaderRenderingIntent(hXYZ, INTENT_PERCEPTUAL); if (hXYZ == NULL) return NULL; cmsSetProfileVersion(hXYZ, 2.1); cmsMLU *mlu0 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)"); cmsMLU *mlu1 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu1, "en", "US", "linear XYZ"); cmsMLU *mlu2 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu2, "en", "US", "Darktable linear XYZ"); cmsWriteTag(hXYZ, cmsSigDeviceMfgDescTag, mlu0); cmsWriteTag(hXYZ, cmsSigDeviceModelDescTag, mlu1); // this will only be displayed when the embedded profile is read by for example GIMP cmsWriteTag(hXYZ, cmsSigProfileDescriptionTag, mlu2); cmsMLUfree(mlu0); cmsMLUfree(mlu1); cmsMLUfree(mlu2); return hXYZ; }
// 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 dt_colorspaces_create_darktable_profile(const char *makermodel) { dt_profiled_colormatrix_t *preset = NULL; for(int k=0; k<dt_profiled_colormatrix_cnt; k++) { if(!strcasecmp(makermodel, dt_profiled_colormatrices[k].makermodel)) { preset = dt_profiled_colormatrices + k; break; } } if(!preset) return NULL; const float wxyz = preset->white[0]+preset->white[1]+preset->white[2]; const float rxyz = preset->rXYZ[0] +preset->rXYZ[1] +preset->rXYZ[2]; const float gxyz = preset->gXYZ[0] +preset->gXYZ[1] +preset->gXYZ[2]; const float bxyz = preset->bXYZ[0] +preset->bXYZ[1] +preset->bXYZ[2]; cmsCIExyY WP = {preset->white[0]/wxyz, preset->white[1]/wxyz, 1.0}; cmsCIExyYTRIPLE XYZPrimaries = { {preset->rXYZ[0]/rxyz, preset->rXYZ[1]/rxyz, 1.0}, {preset->gXYZ[0]/gxyz, preset->gXYZ[1]/gxyz, 1.0}, {preset->bXYZ[0]/bxyz, preset->bXYZ[1]/bxyz, 1.0} }; cmsToneCurve *Gamma[3]; cmsHPROFILE hp; Gamma[0] = Gamma[1] = Gamma[2] = build_linear_gamma(); hp = cmsCreateRGBProfile(&WP, &XYZPrimaries, Gamma); cmsFreeToneCurve(Gamma[0]); if (hp == NULL) return NULL; char name[512]; snprintf(name, 512, "Darktable profiled %s", makermodel); cmsSetProfileVersion(hp, 2.1); cmsMLU *mlu0 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)"); cmsMLU *mlu1 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu1, "en", "US", name); cmsMLU *mlu2 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu2, "en", "US", name); cmsWriteTag(hp, cmsSigDeviceMfgDescTag, mlu0); cmsWriteTag(hp, cmsSigDeviceModelDescTag, mlu1); // this will only be displayed when the embedded profile is read by for example GIMP cmsWriteTag(hp, cmsSigProfileDescriptionTag, mlu2); cmsMLUfree(mlu0); cmsMLUfree(mlu1); cmsMLUfree(mlu2); return hp; }
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; }
// 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 dt_colorspaces_create_xyzmatrix_profile(float mat[3][3]) { // mat: cam -> xyz cmsCIExyY D65; float x[3], y[3]; for(int k=0; k<3; k++) { const float norm = mat[0][k] + mat[1][k] + mat[2][k]; x[k] = mat[0][k] / norm; y[k] = mat[1][k] / norm; } cmsCIExyYTRIPLE CameraPrimaries = { {x[0], y[0], 1.0}, {x[1], y[1], 1.0}, {x[2], y[2], 1.0} }; cmsHPROFILE cmat; cmsWhitePointFromTemp(&D65, 6504.0); cmsToneCurve *Gamma[3]; Gamma[0] = Gamma[1] = Gamma[2] = build_linear_gamma(); cmat = cmsCreateRGBProfile(&D65, &CameraPrimaries, Gamma); if (cmat == NULL) return NULL; cmsFreeToneCurve(Gamma[0]); cmsSetProfileVersion(cmat, 2.1); cmsMLU *mlu0 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)"); cmsMLU *mlu1 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu1, "en", "US", "color matrix built-in"); cmsMLU *mlu2 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu2, "en", "US", "color matrix built-in"); cmsWriteTag(cmat, cmsSigDeviceMfgDescTag, mlu0); cmsWriteTag(cmat, cmsSigDeviceModelDescTag, mlu1); // this will only be displayed when the embedded profile is read by for example GIMP cmsWriteTag(cmat, cmsSigProfileDescriptionTag, mlu2); cmsMLUfree(mlu0); cmsMLUfree(mlu1); cmsMLUfree(mlu2); return cmat; }
cmsHPROFILE dt_colorspaces_create_linear_infrared_profile(void) { // linear rgb with r and b swapped: cmsCIExyY D65; cmsCIExyYTRIPLE Rec709Primaries = { {0.1500, 0.0600, 1.0}, {0.3000, 0.6000, 1.0}, {0.6400, 0.3300, 1.0} }; cmsToneCurve *Gamma[3]; cmsHPROFILE hsRGB; cmsWhitePointFromTemp(&D65, 6504.0); Gamma[0] = Gamma[1] = Gamma[2] = build_linear_gamma(); hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma); cmsFreeToneCurve(Gamma[0]); if (hsRGB == NULL) return NULL; cmsSetProfileVersion(hsRGB, 2.1); cmsMLU *mlu0 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)"); cmsMLU *mlu1 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu1, "en", "US", "linear infrared bgr"); cmsMLU *mlu2 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu2, "en", "US", "Darktable Linear Infrared RGB"); cmsWriteTag(hsRGB, cmsSigDeviceMfgDescTag, mlu0); cmsWriteTag(hsRGB, cmsSigDeviceModelDescTag, mlu1); // this will only be displayed when the embedded profile is read by for example GIMP cmsWriteTag(hsRGB, cmsSigProfileDescriptionTag, mlu2); cmsMLUfree(mlu0); cmsMLUfree(mlu1); cmsMLUfree(mlu2); return hsRGB; }
// 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; }
// Create the ICC virtual profile for adobe rgb space cmsHPROFILE dt_colorspaces_create_adobergb_profile(void) { cmsCIExyY D65; cmsCIExyYTRIPLE AdobePrimaries = { {0.6400, 0.3300, 1.0}, {0.2100, 0.7100, 1.0}, {0.1500, 0.0600, 1.0} }; cmsToneCurve *Gamma22[3]; cmsHPROFILE hAdobeRGB; cmsWhitePointFromTemp(&D65, 6504.0); Gamma22[0] = Gamma22[1] = Gamma22[2] = build_adobergb_gamma(); hAdobeRGB = cmsCreateRGBProfile(&D65, &AdobePrimaries, Gamma22); cmsFreeToneCurve(Gamma22[0]); if (hAdobeRGB == NULL) return NULL; cmsSetProfileVersion(hAdobeRGB, 2.1); cmsMLU *mlu0 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)"); cmsMLU *mlu1 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu1, "en", "US", "AdobeRGB"); cmsMLU *mlu2 = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(mlu2, "en", "US", "Darktable AdobeRGB"); cmsWriteTag(hAdobeRGB, cmsSigDeviceMfgDescTag, mlu0); cmsWriteTag(hAdobeRGB, cmsSigDeviceModelDescTag, mlu1); // this will only be displayed when the embedded profile is read by for example GIMP cmsWriteTag(hAdobeRGB, cmsSigProfileDescriptionTag, mlu2); cmsMLUfree(mlu0); cmsMLUfree(mlu1); cmsMLUfree(mlu2); return hAdobeRGB; }
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; }