// Create an output MPE LUT from agiven profile. Version mismatches are handled here cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent) { cmsTagTypeSignature OriginalType; cmsTagSignature tag16 = PCS2Device16[Intent]; cmsTagSignature tagFloat = PCS2DeviceFloat[Intent]; cmsContext ContextID = cmsGetProfileContextID(hProfile); if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence // Floating point LUT are always V4, so no adjustment is required return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); } // Revert to perceptual if no tag is found if (!cmsIsTag(hProfile, tag16)) { tag16 = PCS2Device16[0]; } if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? // Check profile version and LUT type. Do the necessary adjustments if needed // First read the tag cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); if (Lut == NULL) return NULL; // After reading it, we have info about the original type OriginalType = _cmsGetTagTrueType(hProfile, tag16); // The profile owns the Lut, so we need to copy it Lut = cmsPipelineDup(Lut); if (Lut == NULL) return NULL; // Now it is time for a controversial stuff. I found that for 3D LUTS using // Lab used as indexer space, trilinear interpolation should be used if (cmsGetPCS(hProfile) == cmsSigLabData) ChangeInterpolationToTrilinear(Lut); // We need to adjust data only for Lab and Lut16 type if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) return Lut; // Add a matrix for conversion V4 to V2 Lab PCS cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)); return Lut; } // Lut not found, try to create a matrix-shaper // Check if this is a grayscale profile. if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { // if so, build appropiate conversion tables. // The tables are the PCS iluminant, scaled across GrayTRC return BuildGrayOutputPipeline(hProfile); } // Not gray, create a normal matrix-shaper return BuildRGBOutputMatrixShaper(hProfile); }
// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc // is adjusted here in order to create a LUT that takes care of all those details cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent) { cmsTagTypeSignature OriginalType; cmsTagSignature tag16 = Device2PCS16[Intent]; cmsTagSignature tagFloat = Device2PCSFloat[Intent]; cmsContext ContextID = cmsGetProfileContextID(hProfile); if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence // Floating point LUT are always V4, so no adjustment is required return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); } // Revert to perceptual if no tag is found if (!cmsIsTag(hProfile, tag16)) { tag16 = Device2PCS16[0]; } if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? // Check profile version and LUT type. Do the necessary adjustments if needed // First read the tag cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); if (Lut == NULL) return NULL; // After reading it, we have now info about the original type OriginalType = _cmsGetTagTrueType(hProfile, tag16); // The profile owns the Lut, so we need to copy it Lut = cmsPipelineDup(Lut); // We need to adjust data only for Lab16 on output if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) return Lut; // If the input is Lab, add also a conversion at the begin if (cmsGetColorSpace(hProfile) == cmsSigLabData) cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)); // Add a matrix for conversion V2 to V4 Lab PCS cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)); return Lut; } // Lut was not found, try to create a matrix-shaper // Check if this is a grayscale profile. if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { // if so, build appropiate conversion tables. // The tables are the PCS iluminant, scaled across GrayTRC return BuildGrayInputMatrixPipeline(hProfile); } // Not gray, create a normal matrix-shaper return BuildRGBInputMatrixShaper(hProfile); }
// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The // tag name here may default to AToB0 cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent) { cmsPipeline* Lut; cmsTagTypeSignature OriginalType; cmsTagSignature tag16 = Device2PCS16[Intent]; cmsTagSignature tagFloat = Device2PCSFloat[Intent]; cmsContext ContextID = cmsGetProfileContextID(hProfile); if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence // Floating point LUT are always V4, no adjustment is required return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); } tagFloat = Device2PCSFloat[0]; if (cmsIsTag(hProfile, tagFloat)) { return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); } if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? tag16 = Device2PCS16[0]; if (!cmsIsTag(hProfile, tag16)) return NULL; } // Check profile version and LUT type. Do the necessary adjustments if needed // Read the tag Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); if (Lut == NULL) return NULL; // The profile owns the Lut, so we need to copy it Lut = cmsPipelineDup(Lut); // After reading it, we have info about the original type OriginalType = _cmsGetTagTrueType(hProfile, tag16); // We need to adjust data for Lab16 on output if (OriginalType != cmsSigLut16Type) return Lut; // Here it is possible to get Lab on both sides if (cmsGetPCS(hProfile) == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)); } if (cmsGetColorSpace(hProfile) == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)); } return Lut; }
// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded static cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) { cmsContext ContextID = cmsGetProfileContextID(hProfile); cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); if (Lut == NULL) return NULL; if (spc == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)); } else if (spc == cmsSigXYZData) { cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)); } if (PCS == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)); } else if (PCS == cmsSigXYZData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)); } return Lut; }
static cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) { cmsContext ContextID = cmsGetProfileContextID(hProfile); cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile); if (Lut == NULL) return NULL; // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding, // and since the formatter has already accomodated to 0..1.0, we should undo this change if ( PCS == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)); } else if (PCS == cmsSigXYZData) { cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)); } // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline if ( dataSpace == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)); } else if ( dataSpace == cmsSigXYZData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)); } return Lut; }
/*static cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) { cmsContext ContextID = cmsGetProfileContextID(hProfile); cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); if (Lut == NULL) return NULL; // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding, // and since the formatter has already accomodated to 0..1.0, we should undo this change if ( spc == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)); } else if (spc == cmsSigXYZData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)); } return Lut; } */ static cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) { cmsContext ContextID = cmsGetProfileContextID(hProfile); cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); if (Lut == NULL) return NULL; // input and output of transform are in lcms 0..1 encoding. If XYZ or Lab spaces are used, // these need to be normalized into the appropriate ranges (Lab = 100,0,0, XYZ=1.0,1.0,1.0) if ( spc == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)); } else if (spc == cmsSigXYZData) { cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)); } if ( PCS == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)); } else if( PCS == cmsSigXYZData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)); } return Lut; }
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; }
static int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags) { cmsHPROFILE hLab; cmsHTRANSFORM xform; int i, nChannels; cmsUInt32Number OutputFormat; _cmsTRANSFORM* v; cmsPipeline* DeviceLink; cmsHPROFILE Profiles[3]; cmsCIEXYZ BlackPointAdaptedToD50; cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); cmsUInt32Number InFrm = TYPE_Lab_16; int RelativeEncodingIntent; cmsColorSpaceSignature ColorSpace; hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); if (hLab == NULL) return 0; OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); nChannels = T_CHANNELS(OutputFormat); ColorSpace = cmsGetColorSpace(hProfile); // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision. RelativeEncodingIntent = Intent; if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC) RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC; // Use V4 Lab always Profiles[0] = hLab; Profiles[1] = hProfile; xform = cmsCreateMultiprofileTransformTHR(m ->ContextID, Profiles, 2, TYPE_Lab_DBL, OutputFormat, RelativeEncodingIntent, 0); cmsCloseProfile(hLab); if (xform == NULL) { cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation"); return 0; } // Get a copy of the internal devicelink v = (_cmsTRANSFORM*) xform; DeviceLink = cmsPipelineDup(v ->Lut); if (DeviceLink == NULL) return 0; // We need a CLUT dwFlags |= cmsFLAGS_FORCE_CLUT; _cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); _cmsIOPrintf(m, "<<\n"); _cmsIOPrintf(m, "/ColorRenderingType 1\n"); cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); // Emit headers, etc. EmitWhiteBlackD50(m, &BlackPointAdaptedToD50); EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC); EmitXYZ2Lab(m); // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127, // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to // zero. This would sacrifice a bit of highlights, but failure to do so would cause // scum dot. Ouch. if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) lFixWhite = FALSE; _cmsIOPrintf(m, "/RenderTable "); WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace); _cmsIOPrintf(m, " %d {} bind ", nChannels); for (i=1; i < nChannels; i++) _cmsIOPrintf(m, "dup "); _cmsIOPrintf(m, "]\n"); EmitIntent(m, Intent); _cmsIOPrintf(m, ">>\n"); if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n"); } cmsPipelineFree(DeviceLink); cmsDeleteTransform(xform); return 1; }
// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The // tag name here may default to AToB0 cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent) { cmsPipeline* Lut; cmsTagTypeSignature OriginalType; cmsTagSignature tag16; cmsTagSignature tagFloat; cmsContext ContextID = cmsGetProfileContextID(hProfile); if (Intent < INTENT_PERCEPTUAL || Intent > INTENT_ABSOLUTE_COLORIMETRIC) return NULL; tag16 = Device2PCS16[Intent]; tagFloat = Device2PCSFloat[Intent]; // On named color, take the appropriate tag if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*)cmsReadTag(hProfile, cmsSigNamedColor2Tag); if (nc == NULL) return NULL; Lut = cmsPipelineAlloc(ContextID, 0, 0); if (Lut == NULL) goto Error; if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE))) goto Error; if (cmsGetColorSpace(hProfile) == cmsSigLabData) if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) goto Error; return Lut; Error: cmsPipelineFree(Lut); cmsFreeNamedColorList(nc); return NULL; } if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence // Floating point LUT are always V return _cmsReadFloatDevicelinkTag(hProfile, tagFloat); } tagFloat = Device2PCSFloat[0]; if (cmsIsTag(hProfile, tagFloat)) { return cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat)); } if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? tag16 = Device2PCS16[0]; if (!cmsIsTag(hProfile, tag16)) return NULL; } // Check profile version and LUT type. Do the necessary adjustments if needed // Read the tag Lut = (cmsPipeline*)cmsReadTag(hProfile, tag16); if (Lut == NULL) return NULL; // The profile owns the Lut, so we need to copy it Lut = cmsPipelineDup(Lut); if (Lut == NULL) return NULL; // Now it is time for a controversial stuff. I found that for 3D LUTS using // Lab used as indexer space, trilinear interpolation should be used if (cmsGetPCS(hProfile) == cmsSigLabData) ChangeInterpolationToTrilinear(Lut); // After reading it, we have info about the original type OriginalType = _cmsGetTagTrueType(hProfile, tag16); // We need to adjust data for Lab16 on output if (OriginalType != cmsSigLut16Type) return Lut; // Here it is possible to get Lab on both sides if (cmsGetColorSpace(hProfile) == cmsSigLabData) { if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) goto Error2; } if (cmsGetPCS(hProfile) == cmsSigLabData) { if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) goto Error2; } return Lut; Error2: cmsPipelineFree(Lut); return NULL; }
// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc // is adjusted here in order to create a LUT that takes care of all those details. // We add intent = -1 as a way to read matrix shaper always, no matter of other LUT cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent) { cmsTagTypeSignature OriginalType; cmsTagSignature tag16; cmsTagSignature tagFloat; cmsContext ContextID = cmsGetProfileContextID(hProfile); // On named color, take the appropriate tag if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { cmsPipeline* Lut; cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag); if (nc == NULL) return NULL; Lut = cmsPipelineAlloc(ContextID, 0, 0); if (Lut == NULL) { cmsFreeNamedColorList(nc); return NULL; } if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) || !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) { cmsPipelineFree(Lut); return NULL; } return Lut; } // This is an attempt to reuse this function to retrieve the matrix-shaper as pipeline no // matter other LUT are present and have precedence. Intent = -1 means just this. if (Intent >= INTENT_PERCEPTUAL && Intent <= INTENT_ABSOLUTE_COLORIMETRIC) { tag16 = Device2PCS16[Intent]; tagFloat = Device2PCSFloat[Intent]; if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence // Floating point LUT are always V4, but the encoding range is no // longer 0..1.0, so we need to add an stage depending on the color space return _cmsReadFloatInputTag(hProfile, tagFloat); } // Revert to perceptual if no tag is found if (!cmsIsTag(hProfile, tag16)) { tag16 = Device2PCS16[0]; } if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? // Check profile version and LUT type. Do the necessary adjustments if needed // First read the tag cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); if (Lut == NULL) return NULL; // After reading it, we have now info about the original type OriginalType = _cmsGetTagTrueType(hProfile, tag16); // The profile owns the Lut, so we need to copy it Lut = cmsPipelineDup(Lut); // We need to adjust data only for Lab16 on output if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) return Lut; // If the input is Lab, add also a conversion at the begin if (cmsGetColorSpace(hProfile) == cmsSigLabData && !cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) goto Error; // Add a matrix for conversion V2 to V4 Lab PCS if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) goto Error; return Lut; Error: cmsPipelineFree(Lut); return NULL; } } // Lut was not found, try to create a matrix-shaper // Check if this is a grayscale profile. if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { // if so, build appropriate conversion tables. // The tables are the PCS iluminant, scaled across GrayTRC return BuildGrayInputMatrixPipeline(hProfile); } // Not gray, create a normal matrix-shaper return BuildRGBInputMatrixShaper(hProfile); }
// 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; }