// 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); }
static cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) { cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut; cmsColorSpaceSignature PostColorSpace; int i; if (nProfiles <= 0) return FALSE; if (hProfiles[0] == NULL) return FALSE; *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]); for (i=0; i < nProfiles; i++) { cmsProfileClassSignature cls; cmsHPROFILE hProfile = hProfiles[i]; int lIsInput = (PostColorSpace != cmsSigXYZData) && (PostColorSpace != cmsSigLabData); if (hProfile == NULL) return FALSE; cls = cmsGetDeviceClass(hProfile); if (cls == cmsSigNamedColorClass) { ColorSpaceIn = cmsSig1colorData; ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile); } else if (lIsInput || (cls == cmsSigLinkClass)) { ColorSpaceIn = cmsGetColorSpace(hProfile); ColorSpaceOut = cmsGetPCS(hProfile); } else { ColorSpaceIn = cmsGetPCS(hProfile); ColorSpaceOut = cmsGetColorSpace(hProfile); } if (i==0) *Input = ColorSpaceIn; PostColorSpace = ColorSpaceOut; } *Output = PostColorSpace; return TRUE; }
/*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 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; }
// 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 int GetDevicelinkColorSpace(cmsHPROFILE hProfile) { cmsColorSpaceSignature ProfileSpace = cmsGetPCS(hProfile); return _cmsLCMScolorSpace(ProfileSpace); }
static PyObject* cms_profile_getattro(CmsProfileObject* self, PyObject* name) { PyObject* name_bytes; char* name_string; if (!PyUnicode_Check(name)) return NULL; if (!(name_bytes = PyUnicode_AsASCIIString(name))) return NULL; if (!(name_string = PyBytes_AsString(name_bytes))) return NULL; if (!strcmp(name_string, "product_name")) return PyUnicode_FromFormat("%s", cmsTakeProductName(self->profile)); if (!strcmp(name_string, "product_desc")) return PyUnicode_FromFormat("%s", cmsTakeProductDesc(self->profile)); if (!strcmp(name_string, "product_info")) return PyUnicode_FromFormat("%s", cmsTakeProductInfo(self->profile)); if (!strcmp(name_string, "rendering_intent")) return PyLong_FromLong(cmsTakeRenderingIntent(self->profile)); if (!strcmp(name_string, "pcs")) return PyUnicode_FromFormat("%s", findICmode(cmsGetPCS(self->profile))); if (!strcmp(name_string, "color_space")) return PyUnicode_FromFormat("%s", findICmode(cmsGetColorSpace(self->profile))); /* FIXME: add more properties (creation_datetime etc) */ return PyObject_GenericGetAttr((PyObject*)self, name); }
static cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile) { cmsToneCurve *GrayTRC, *RevGrayTRC; cmsPipeline* Lut; cmsContext ContextID = cmsGetProfileContextID(hProfile); GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); if (GrayTRC == NULL) return NULL; RevGrayTRC = cmsReverseToneCurve(GrayTRC); if (RevGrayTRC == NULL) return NULL; Lut = cmsPipelineAlloc(ContextID, 3, 1); if (Lut == NULL) { cmsFreeToneCurve(RevGrayTRC); return NULL; } if (cmsGetPCS(hProfile) == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)); } else { cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL)); } cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC)); cmsFreeToneCurve(RevGrayTRC); return Lut; }
/* Get the number of output channels for the profile */ int gscms_get_output_channel_count(gcmmhprofile_t profile) { icColorSpaceSignature colorspace; colorspace = cmsGetPCS(profile); return(_cmsChannelsOf(colorspace)); }
// 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); }
static cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile) { cmsPipeline* Lut; cmsToneCurve *Shapes[3], *InvShapes[3]; cmsMAT3 Mat, Inv; int i, j; cmsContext ContextID = cmsGetProfileContextID(hProfile); if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL; if (!_cmsMAT3inverse(&Mat, &Inv)) return NULL; // XYZ PCS in encoded in 1.15 format, and the matrix input should come in 0..0xffff range, so // we need to adjust the input by a << 1 to obtain a 1.16 fixed and then by a factor of // (0xffff/0x10000) to put data in 0..0xffff range. Total factor is (2.0*65535.0)/65536.0; for (i=0; i < 3; i++) for (j=0; j < 3; j++) Inv.v[i].n[j] *= OutpAdj; Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); if (!Shapes[0] || !Shapes[1] || !Shapes[2]) return NULL; InvShapes[0] = cmsReverseToneCurve(Shapes[0]); InvShapes[1] = cmsReverseToneCurve(Shapes[1]); InvShapes[2] = cmsReverseToneCurve(Shapes[2]); if (!InvShapes[0] || !InvShapes[1] || !InvShapes[2]) { return NULL; } Lut = cmsPipelineAlloc(ContextID, 3, 3); if (Lut != NULL) { // Note that it is certainly possible a single profile would have a LUT based // tag for output working in lab and a matrix-shaper for the fallback cases. // This is not allowed by the spec, but this code is tolerant to those cases if (cmsGetPCS(hProfile) == cmsSigLabData) { cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLab2XYZ(ContextID)); } cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)); cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes)); } cmsFreeToneCurveTriple(InvShapes); return Lut; }
/* Get the link from the CMS, but include proofing and/or a device link profile. */ gcmmhlink_t gscms_get_link_proof_devlink(gcmmhprofile_t lcms_srchandle, gcmmhprofile_t lcms_proofhandle, gcmmhprofile_t lcms_deshandle, gcmmhprofile_t lcms_devlinkhandle, gsicc_rendering_param_t *rendering_params, bool src_dev_link, int cmm_flags, gs_memory_t *mem) { DWORD src_data_type,des_data_type; icColorSpaceSignature src_color_space,des_color_space; int src_nChannels,des_nChannels; int lcms_src_color_space, lcms_des_color_space; cmsHPROFILE hProfiles[5]; int nProfiles = 0; /* First handle all the source stuff */ src_color_space = cmsGetColorSpace(lcms_srchandle); lcms_src_color_space = _cmsLCMScolorSpace(src_color_space); /* littlecms returns -1 for types it does not (but should) understand */ if (lcms_src_color_space < 0) lcms_src_color_space = 0; src_nChannels = _cmsChannelsOf(src_color_space); /* For now, just do single byte data, interleaved. We can change this when we use the transformation. */ src_data_type = (COLORSPACE_SH(lcms_src_color_space)| CHANNELS_SH(src_nChannels)|BYTES_SH(2)); if (lcms_deshandle != NULL) { des_color_space = cmsGetColorSpace(lcms_deshandle); } else { /* We must have a device link profile. */ des_color_space = cmsGetPCS(lcms_deshandle); } lcms_des_color_space = _cmsLCMScolorSpace(des_color_space); if (lcms_des_color_space < 0) lcms_des_color_space = 0; des_nChannels = _cmsChannelsOf(des_color_space); des_data_type = (COLORSPACE_SH(lcms_des_color_space)| CHANNELS_SH(des_nChannels)|BYTES_SH(2)); /* lcms proofing transform has a clunky API and can't include the device link profile if we have both. So use cmsCreateMultiprofileTransform instead and round trip the proofing profile. */ hProfiles[nProfiles++] = lcms_srchandle; if (lcms_proofhandle != NULL) { hProfiles[nProfiles++] = lcms_proofhandle; hProfiles[nProfiles++] = lcms_proofhandle; } hProfiles[nProfiles++] = lcms_deshandle; if (lcms_devlinkhandle != NULL) { hProfiles[nProfiles++] = lcms_devlinkhandle; } return(cmsCreateMultiprofileTransform(hProfiles, nProfiles, src_data_type, des_data_type, rendering_params->rendering_intent, (cmm_flags | cmsFLAGS_BLACKPOINTCOMPENSATION | cmsFLAGS_HIGHRESPRECALC | cmsFLAGS_NOTCACHE))); }
// 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; }
// Gray input pipeline static cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile) { cmsToneCurve *GrayTRC; cmsPipeline* Lut; cmsContext ContextID = cmsGetProfileContextID(hProfile); GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); if (GrayTRC == NULL) return NULL; Lut = cmsPipelineAlloc(ContextID, 1, 3); if (Lut == NULL) goto Error; if (cmsGetPCS(hProfile) == cmsSigLabData) { // In this case we implement the profile as an identity matrix plus 3 tone curves cmsUInt16Number Zero[2] = { 0x8080, 0x8080 }; cmsToneCurve* EmptyTab; cmsToneCurve* LabCurves[3]; EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); if (EmptyTab == NULL) goto Error; LabCurves[0] = GrayTRC; LabCurves[1] = EmptyTab; LabCurves[2] = EmptyTab; if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)) || !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) { cmsFreeToneCurve(EmptyTab); goto Error; } cmsFreeToneCurve(EmptyTab); } else { if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)) || !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL))) goto Error; } return Lut; Error: cmsFreeToneCurve(GrayTRC); cmsPipelineFree(Lut); return NULL; }
// RGB Matrix shaper static cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile) { cmsPipeline* Lut; cmsMAT3 Mat; cmsToneCurve *Shapes[3]; cmsContext ContextID = cmsGetProfileContextID(hProfile); int i, j; if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL; // XYZ PCS in encoded in 1.15 format, and the matrix output comes in 0..0xffff range, so // we need to adjust the output by a factor of (0x10000/0xffff) to put data in // a 1.16 range, and then a >> 1 to obtain 1.15. The total factor is (65536.0)/(65535.0*2) for (i=0; i < 3; i++) for (j=0; j < 3; j++) Mat.v[i].n[j] *= InpAdj; Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); if (!Shapes[0] || !Shapes[1] || !Shapes[2]) return NULL; Lut = cmsPipelineAlloc(ContextID, 3, 3); if (Lut != NULL) { if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) || !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL))) goto Error; // Note that it is certainly possible a single profile would have a LUT based // tag for output working in lab and a matrix-shaper for the fallback cases. // This is not allowed by the spec, but this code is tolerant to those cases if (cmsGetPCS(hProfile) == cmsSigLabData) { if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocXYZ2Lab(ContextID))) goto Error; } } return Lut; Error: cmsPipelineFree(Lut); return NULL; }
static PyObject* cms_profile_getattr(CmsProfileObject* self, char* name) { #ifdef PY3 if (!strcmp(name, "product_name")) return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile)); if (!strcmp(name, "product_desc")) return PyUnicode_DecodeFSDefault(cmsTakeProductDesc(self->profile)); if (!strcmp(name, "product_info")) return PyUnicode_DecodeFSDefault(cmsTakeProductInfo(self->profile)); if (!strcmp(name, "rendering_intent")) return PyLong_FromLong(cmsTakeRenderingIntent(self->profile)); if (!strcmp(name, "pcs")) return PyUnicode_DecodeFSDefault(findICmode(cmsGetPCS(self->profile))); if (!strcmp(name, "color_space")) return PyUnicode_DecodeFSDefault(findICmode(cmsGetColorSpace(self->profile))); /* FIXME: add more properties (creation_datetime etc) */ return PyObject_GenericGetAttr((PyObject *)self, PyUnicode_DecodeFSDefault(name)); #else if (!strcmp(name, "product_name")) return PyString_FromString(cmsTakeProductName(self->profile)); if (!strcmp(name, "product_desc")) return PyString_FromString(cmsTakeProductDesc(self->profile)); if (!strcmp(name, "product_info")) return PyString_FromString(cmsTakeProductInfo(self->profile)); if (!strcmp(name, "rendering_intent")) return PyInt_FromLong(cmsTakeRenderingIntent(self->profile)); if (!strcmp(name, "pcs")) return PyString_FromString(findICmode(cmsGetPCS(self->profile))); if (!strcmp(name, "color_space")) return PyString_FromString(findICmode(cmsGetColorSpace(self->profile))); /* FIXME: add more properties (creation_datetime etc) */ return Py_FindMethod(cms_profile_methods, (PyObject*) self, name); #endif }
static cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) { _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; cmsHPROFILE hICC; cmsCIEXYZ WhitePoint; int i, nColors; size_t Size; LPcmsNAMEDCOLORLIST nc2; hICC = _cmsCreateProfilePlaceholder(); if (hICC == NULL) return NULL; cmsSetRenderingIntent(hICC, v -> Intent); cmsSetDeviceClass(hICC, icSigNamedColorClass); cmsSetColorSpace(hICC, v ->ExitColorSpace); cmsSetPCS(hICC, cmsGetPCS(v ->InputProfile)); cmsTakeMediaWhitePoint(&WhitePoint, v ->InputProfile); cmsAddTag(hICC, icSigMediaWhitePointTag, &WhitePoint); cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS"); cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Named color Device link"); cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Named color Device link"); nColors = cmsNamedColorCount(xform); nc2 = cmsAllocNamedColorList(nColors); Size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * (nColors-1)); CopyMemory(nc2, v->NamedColorList, Size); nc2 ->ColorantCount = _cmsChannelsOf(v ->ExitColorSpace); for (i=0; i < nColors; i++) { cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); } cmsAddTag(hICC, icSigNamedColor2Tag, (void*) nc2); cmsFreeNamedColorList(nc2); return hICC; }
/* Get the link from the CMS. TODO: Add error checking */ gcmmhlink_t gscms_get_link(gcmmhprofile_t lcms_srchandle, gcmmhprofile_t lcms_deshandle, gsicc_rendering_param_t *rendering_params) { DWORD src_data_type,des_data_type; icColorSpaceSignature src_color_space,des_color_space; int src_nChannels,des_nChannels; int lcms_src_color_space, lcms_des_color_space; /* Check for case of request for a transfrom from a device link profile in that case, the destination profile is NULL */ /* First handle all the source stuff */ src_color_space = cmsGetColorSpace(lcms_srchandle); lcms_src_color_space = _cmsLCMScolorSpace(src_color_space); /* littlecms returns -1 for types it does not (but should) understand */ if (lcms_src_color_space < 0) lcms_src_color_space = 0; src_nChannels = _cmsChannelsOf(src_color_space); /* For now, just do single byte data, interleaved. We can change this when we use the transformation. */ src_data_type = (COLORSPACE_SH(lcms_src_color_space)| CHANNELS_SH(src_nChannels)|BYTES_SH(2)); if (lcms_deshandle != NULL) { des_color_space = cmsGetColorSpace(lcms_deshandle); } else { /* We must have a device link profile. */ des_color_space = cmsGetPCS(lcms_deshandle); } lcms_des_color_space = _cmsLCMScolorSpace(des_color_space); if (lcms_des_color_space < 0) lcms_des_color_space = 0; des_nChannels = _cmsChannelsOf(des_color_space); des_data_type = (COLORSPACE_SH(lcms_des_color_space)| CHANNELS_SH(des_nChannels)|BYTES_SH(2)); /* Create the link */ return(cmsCreateTransform(lcms_srchandle, src_data_type, lcms_deshandle, des_data_type, rendering_params->rendering_intent, (cmsFLAGS_BLACKPOINTCOMPENSATION | cmsFLAGS_HIGHRESPRECALC))); /* cmsFLAGS_HIGHRESPRECALC) cmsFLAGS_NOTPRECALC cmsFLAGS_LOWRESPRECALC*/ }
static void OpenTransforms(int argc, char *argv[]) { DWORD dwIn, dwOut, dwFlags; if (lMultiProfileChain) { int i; cmsHTRANSFORM hTmp; nProfiles = argc - xoptind; for (i=0; i < nProfiles; i++) { hProfiles[i] = OpenProfile(argv[i+xoptind]); } // Create a temporary devicelink hTmp = cmsCreateMultiprofileTransform(hProfiles, nProfiles, 0, 0, Intent, GetFlags()); hInput = cmsTransform2DeviceLink(hTmp, 0); hOutput = NULL; cmsDeleteTransform(hTmp); InputColorSpace = cmsGetColorSpace(hInput); OutputColorSpace = cmsGetPCS(hInput); lIsDeviceLink = TRUE; } else if (lIsDeviceLink) { hInput = cmsOpenProfileFromFile(cInProf, "r"); hOutput = NULL; InputColorSpace = cmsGetColorSpace(hInput); OutputColorSpace = cmsGetPCS(hInput); } else { hInput = OpenProfile(cInProf); hOutput = OpenProfile(cOutProf); InputColorSpace = cmsGetColorSpace(hInput); OutputColorSpace = cmsGetColorSpace(hOutput); if (cmsGetDeviceClass(hInput) == icSigLinkClass || cmsGetDeviceClass(hOutput) == icSigLinkClass) FatalError("Use %cl flag for devicelink profiles!\n", SW); } if (Verbose) { mexPrintf("From: %s\n", cmsTakeProductName(hInput)); if (hOutput) mexPrintf("To : %s\n\n", cmsTakeProductName(hOutput)); } OutputChannels = _cmsChannelsOf(OutputColorSpace); InputChannels = _cmsChannelsOf(InputColorSpace); dwIn = MakeFormatDescriptor(InputColorSpace, nBytesDepth); dwOut = MakeFormatDescriptor(OutputColorSpace, nBytesDepth); dwFlags = GetFlags(); if (cProofing != NULL) { hProof = OpenProfile(cProofing); dwFlags |= cmsFLAGS_SOFTPROOFING; } hColorTransform = cmsCreateProofingTransform(hInput, dwIn, hOutput, dwOut, hProof, Intent, ProofingIntent, dwFlags); }
// Creates all needed color transforms static cmsBool OpenTransforms(void) { cmsHPROFILE hInput, hOutput, hProof; cmsUInt32Number dwIn, dwOut, dwFlags; cmsNAMEDCOLORLIST* List; int i; // We don't need cache dwFlags = cmsFLAGS_NOCACHE; if (lIsDeviceLink) { hInput = OpenStockProfile(0, cInProf); if (hInput == NULL) return FALSE; hOutput = NULL; hProof = NULL; if (cmsGetDeviceClass(hInput) == cmsSigNamedColorClass) { OutputColorSpace = cmsGetColorSpace(hInput); InputColorSpace = cmsGetPCS(hInput); } else { InputColorSpace = cmsGetColorSpace(hInput); OutputColorSpace = cmsGetPCS(hInput); } // Read colorant tables if present if (cmsIsTag(hInput, cmsSigColorantTableTag)) { List = cmsReadTag(hInput, cmsSigColorantTableTag); InputColorant = cmsDupNamedColorList(List); InputRange = 1; } else InputColorant = ComponentNames(InputColorSpace, TRUE); if (cmsIsTag(hInput, cmsSigColorantTableOutTag)){ List = cmsReadTag(hInput, cmsSigColorantTableOutTag); OutputColorant = cmsDupNamedColorList(List); OutputRange = 1; } else OutputColorant = ComponentNames(OutputColorSpace, FALSE); } else { hInput = OpenStockProfile(0, cInProf); if (hInput == NULL) return FALSE; hOutput = OpenStockProfile(0, cOutProf); if (hOutput == NULL) return FALSE; hProof = NULL; if (cmsGetDeviceClass(hInput) == cmsSigLinkClass || cmsGetDeviceClass(hOutput) == cmsSigLinkClass) FatalError("Use %cl flag for devicelink profiles!\n", SW); InputColorSpace = cmsGetColorSpace(hInput); OutputColorSpace = cmsGetColorSpace(hOutput); // Read colorant tables if present if (cmsIsTag(hInput, cmsSigColorantTableTag)) { List = cmsReadTag(hInput, cmsSigColorantTableTag); InputColorant = cmsDupNamedColorList(List); if (cmsNamedColorCount(InputColorant) <= 3) SetRange(255, TRUE); else SetRange(1, TRUE); // Inks are already divided by 100 in the formatter } else InputColorant = ComponentNames(InputColorSpace, TRUE); if (cmsIsTag(hOutput, cmsSigColorantTableTag)){ List = cmsReadTag(hOutput, cmsSigColorantTableTag); OutputColorant = cmsDupNamedColorList(List); if (cmsNamedColorCount(OutputColorant) <= 3) SetRange(255, FALSE); else SetRange(1, FALSE); // Inks are already divided by 100 in the formatter } else OutputColorant = ComponentNames(OutputColorSpace, FALSE); if (cProofing != NULL) { hProof = OpenStockProfile(0, cProofing); if (hProof == NULL) return FALSE; dwFlags |= cmsFLAGS_SOFTPROOFING; } } // Print information on profiles if (Verbose > 2) { printf("Profile:\n"); PrintProfileInformation(hInput); if (hOutput) { printf("Output profile:\n"); PrintProfileInformation(hOutput); } if (hProof != NULL) { printf("Proofing profile:\n"); PrintProfileInformation(hProof); } } // Input is always in floating point dwIn = cmsFormatterForColorspaceOfProfile(hInput, 0, TRUE); if (lIsDeviceLink) { dwOut = cmsFormatterForPCSOfProfile(hInput, lIsFloat ? 0 : 2, lIsFloat); } else { // 16 bits or floating point (only on output) dwOut = cmsFormatterForColorspaceOfProfile(hOutput, lIsFloat ? 0 : 2, lIsFloat); } // For named color, there is a specialized formatter if (cmsGetDeviceClass(hInput) == cmsSigNamedColorClass) { dwIn = TYPE_NAMED_COLOR_INDEX; InputNamedColor = TRUE; } // Precision mode switch (PrecalcMode) { case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break; case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break; case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break; case 1: break; default: FatalError("Unknown precalculation mode '%d'", PrecalcMode); } if (BlackPointCompensation) dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; if (GamutCheck) { cmsUInt16Number Alarm[cmsMAXCHANNELS]; if (hProof == NULL) FatalError("I need proofing profile -p for gamut checking!"); for (i=0; i < cmsMAXCHANNELS; i++) Alarm[i] = 0xFFFF; cmsSetAlarmCodes(Alarm); dwFlags |= cmsFLAGS_GAMUTCHECK; } // The main transform hTrans = cmsCreateProofingTransform(hInput, dwIn, hOutput, dwOut, hProof, Intent, ProofingIntent, dwFlags); if (hProof) cmsCloseProfile(hProof); if (hTrans == NULL) return FALSE; // PCS Dump if requested hTransXYZ = NULL; hTransLab = NULL; if (hOutput && Verbose > 1) { cmsHPROFILE hXYZ = cmsCreateXYZProfile(); cmsHPROFILE hLab = cmsCreateLab4Profile(NULL); hTransXYZ = cmsCreateTransform(hInput, dwIn, hXYZ, lIsFloat ? TYPE_XYZ_DBL : TYPE_XYZ_16, Intent, cmsFLAGS_NOCACHE); if (hTransXYZ == NULL) return FALSE; hTransLab = cmsCreateTransform(hInput, dwIn, hLab, lIsFloat? TYPE_Lab_DBL : TYPE_Lab_16, Intent, cmsFLAGS_NOCACHE); if (hTransLab == NULL) return FALSE; cmsCloseProfile(hXYZ); cmsCloseProfile(hLab); } if (hInput) cmsCloseProfile(hInput); if (hOutput) cmsCloseProfile(hOutput); return TRUE; }
// 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); }
static PyObject* cms_profile_getattr_pcs(CmsProfileObject* self, void* closure) { return PyUnicode_DecodeFSDefault(findICmode(cmsGetPCS(self->profile))); }
static int TransformImage(TIFF* in, TIFF* out, const char *cDefInpProf, const char *cOutProf) { cmsHPROFILE hIn, hOut, hProof, hInkLimit = NULL; cmsHTRANSFORM xform; DWORD wInput, wOutput; int OutputColorSpace; int bps = (Width16 ? 2 : 1); DWORD dwFlags = 0; int nPlanes; // Observer adaptation state (only meaningful on absolute colorimetric intent) cmsSetAdaptationState(ObserverAdaptationState); if (EmbedProfile && cOutProf) DoEmbedProfile(out, cOutProf); if (BlackWhiteCompensation) dwFlags |= cmsFLAGS_WHITEBLACKCOMPENSATION; if (PreserveBlack) { dwFlags |= cmsFLAGS_PRESERVEBLACK; if (PrecalcMode == 0) PrecalcMode = 1; } switch (PrecalcMode) { case 0: dwFlags |= cmsFLAGS_NOTPRECALC; break; case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break; case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break; case 1: break; default: FatalError("Unknown precalculation mode '%d'", PrecalcMode); } if (GamutCheck) dwFlags |= cmsFLAGS_GAMUTCHECK; hProof = NULL; hOut = NULL; if (lIsDeviceLink) { hIn = cmsOpenProfileFromFile(cDefInpProf, "r"); } else { hIn = GetTIFFProfile(in); if (hIn == NULL) hIn = OpenStockProfile(cDefInpProf); hOut = OpenStockProfile(cOutProf); if (cProofing != NULL) { hProof = OpenStockProfile(cProofing); dwFlags |= cmsFLAGS_SOFTPROOFING; } } // Take input color space wInput = GetInputPixelType(in); // Assure both, input profile and input TIFF are on same colorspace if (_cmsLCMScolorSpace(cmsGetColorSpace(hIn)) != (int) T_COLORSPACE(wInput)) FatalError("Input profile is not operating in proper color space"); if (!lIsDeviceLink) OutputColorSpace = _cmsLCMScolorSpace(cmsGetColorSpace(hOut)); else OutputColorSpace = _cmsLCMScolorSpace(cmsGetPCS(hIn)); wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace, bps); WriteOutputTags(out, OutputColorSpace, bps); CopyOtherTags(in, out); // Ink limit if (InkLimit != 400.0 && (OutputColorSpace == PT_CMYK || OutputColorSpace == PT_CMY)) { cmsHPROFILE hProfiles[10]; int nProfiles = 0; hInkLimit = cmsCreateInkLimitingDeviceLink(cmsGetColorSpace(hOut), InkLimit); hProfiles[nProfiles++] = hIn; if (hProof) { hProfiles[nProfiles++] = hProof; hProfiles[nProfiles++] = hProof; } hProfiles[nProfiles++] = hOut; hProfiles[nProfiles++] = hInkLimit; xform = cmsCreateMultiprofileTransform(hProfiles, nProfiles, wInput, wOutput, Intent, dwFlags); } else { xform = cmsCreateProofingTransform(hIn, wInput, hOut, wOutput, hProof, Intent, ProofingIntent, dwFlags); } // Planar stuff if (T_PLANAR(wInput)) nPlanes = T_CHANNELS(wInput) + T_EXTRA(wInput); else nPlanes = 1; // TIFF Lab of 8 bits need special handling if (wInput == TYPE_Lab_8 && !InputLabUsingICC && cInpProf != NULL && stricmp(cInpProf, "*Lab") == 0) { cmsSetUserFormatters(xform, TYPE_Lab_8, UnrollTIFFLab8, TYPE_Lab_8, NULL); } if (wOutput == TYPE_Lab_8 && cOutProf != NULL && stricmp(cOutProf, "*Lab") == 0) { cmsSetUserFormatters(xform, TYPE_Lab_8, NULL, TYPE_Lab_8, PackTIFFLab8); } // Handle tile by tile or strip by strip if (TIFFIsTiled(in)) { TileBasedXform(xform, in, out, nPlanes); } else { StripBasedXform(xform, in, out, nPlanes); } cmsDeleteTransform(xform); cmsCloseProfile(hIn); cmsCloseProfile(hOut); if (hInkLimit) cmsCloseProfile(hInkLimit); if (hProof) cmsCloseProfile(hProof); TIFFWriteDirectory(out); return 1; }
/* Get the link from the CMS. TODO: Add error checking */ gcmmhlink_t gscms_get_link(gcmmhprofile_t lcms_srchandle, gcmmhprofile_t lcms_deshandle, gsicc_rendering_param_t *rendering_params, int cmm_flags, gs_memory_t *memory) { cmsUInt32Number src_data_type,des_data_type; cmsColorSpaceSignature src_color_space,des_color_space; int src_nChannels,des_nChannels; int lcms_src_color_space, lcms_des_color_space; unsigned int flag; /* Check for case of request for a transfrom from a device link profile in that case, the destination profile is NULL */ /* First handle all the source stuff */ src_color_space = cmsGetColorSpace(lcms_srchandle); lcms_src_color_space = _cmsLCMScolorSpace(src_color_space); /* littlecms returns -1 for types it does not (but should) understand */ if (lcms_src_color_space < 0) lcms_src_color_space = 0; src_nChannels = cmsChannelsOf(src_color_space); /* For now, just do single byte data, interleaved. We can change this when we use the transformation. */ src_data_type = (COLORSPACE_SH(lcms_src_color_space)| CHANNELS_SH(src_nChannels)|BYTES_SH(2)); #if 0 src_data_type = src_data_type | ENDIAN16_SH(1); #endif if (lcms_deshandle != NULL) { des_color_space = cmsGetColorSpace(lcms_deshandle); } else { /* We must have a device link profile. */ des_color_space = cmsGetPCS(lcms_deshandle); } lcms_des_color_space = _cmsLCMScolorSpace(des_color_space); if (lcms_des_color_space < 0) lcms_des_color_space = 0; des_nChannels = cmsChannelsOf(des_color_space); des_data_type = (COLORSPACE_SH(lcms_des_color_space)| CHANNELS_SH(des_nChannels)|BYTES_SH(2)); /* endian */ #if 0 des_data_type = des_data_type | ENDIAN16_SH(1); #endif /* Set up the flags */ flag = cmsFLAGS_HIGHRESPRECALC; if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); } if (rendering_params->preserve_black == gsBLACKPRESERVE_KONLY) { switch (rendering_params->rendering_intent) { case INTENT_PERCEPTUAL: rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_PERCEPTUAL; break; case INTENT_RELATIVE_COLORIMETRIC: rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC; break; case INTENT_SATURATION: rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_SATURATION; break; default: break; } } if (rendering_params->preserve_black == gsBLACKPRESERVE_KPLANE) { switch (rendering_params->rendering_intent) { case INTENT_PERCEPTUAL: rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_PERCEPTUAL; break; case INTENT_RELATIVE_COLORIMETRIC: rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC; break; case INTENT_SATURATION: rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_SATURATION; break; default: break; } } /* Create the link */ return cmsCreateTransformTHR((cmsContext)memory, lcms_srchandle, src_data_type, lcms_deshandle, des_data_type, rendering_params->rendering_intent, flag | cmm_flags); /* cmsFLAGS_HIGHRESPRECALC) cmsFLAGS_NOTPRECALC cmsFLAGS_LOWRESPRECALC*/ }
// Default handler for ICC-style intents static cmsPipeline* DefaultICCintents(cmsContext ContextID, cmsUInt32Number nProfiles, cmsUInt32Number TheIntents[], cmsHPROFILE hProfiles[], cmsBool BPC[], cmsFloat64Number AdaptationStates[], cmsUInt32Number dwFlags) { cmsPipeline* Lut = NULL; cmsPipeline* Result; cmsHPROFILE hProfile; cmsMAT3 m; cmsVEC3 off; cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut, CurrentColorSpace; cmsProfileClassSignature ClassSig; cmsUInt32Number i, Intent; // For safety if (nProfiles == 0) return NULL; // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined' Result = cmsPipelineAlloc(ContextID, 0, 0); if (Result == NULL) return NULL; CurrentColorSpace = cmsGetColorSpace(hProfiles[0]); for (i=0; i < nProfiles; i++) { cmsBool lIsDeviceLink, lIsInput; hProfile = hProfiles[i]; ClassSig = cmsGetDeviceClass(hProfile); lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass ); // First profile is used as input unless devicelink or abstract if ((i == 0) && !lIsDeviceLink) { lIsInput = TRUE; } else { // Else use profile in the input direction if current space is not PCS lIsInput = (CurrentColorSpace != cmsSigXYZData) && (CurrentColorSpace != cmsSigLabData); } Intent = TheIntents[i]; if (lIsInput || lIsDeviceLink) { ColorSpaceIn = cmsGetColorSpace(hProfile); ColorSpaceOut = cmsGetPCS(hProfile); } else { ColorSpaceIn = cmsGetPCS(hProfile); ColorSpaceOut = cmsGetColorSpace(hProfile); } if (!ColorSpaceIsCompatible(ColorSpaceIn, CurrentColorSpace)) { cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "ColorSpace mismatch"); goto Error; } // If devicelink is found, then no custom intent is allowed and we can // read the LUT to be applied. Settings don't apply here. if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) { // Get the involved LUT from the profile Lut = _cmsReadDevicelinkLUT(hProfile, Intent); if (Lut == NULL) goto Error; // What about abstract profiles? if (ClassSig == cmsSigAbstractClass && i > 0) { if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; } else { _cmsMAT3identity(&m); _cmsVEC3init(&off, 0, 0, 0); } if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; } else { if (lIsInput) { // Input direction means non-pcs connection, so proceed like devicelinks Lut = _cmsReadInputLUT(hProfile, Intent); if (Lut == NULL) goto Error; } else { // Output direction means PCS connection. Intent may apply here Lut = _cmsReadOutputLUT(hProfile, Intent); if (Lut == NULL) goto Error; if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; } } // Concatenate to the output LUT if (!cmsPipelineCat(Result, Lut)) goto Error; cmsPipelineFree(Lut); Lut = NULL; // Update current space CurrentColorSpace = ColorSpaceOut; } return Result; Error: if (Lut != NULL) cmsPipelineFree(Lut); if (Result != NULL) cmsPipelineFree(Result); return NULL; cmsUNUSED_PARAMETER(dwFlags); }
/*#define DEBUG_PROFILE*/ void color_apply_icc_profile(opj_image_t *image) { cmsHPROFILE in_prof, out_prof; cmsHTRANSFORM transform; cmsColorSpaceSignature in_space, out_space; cmsUInt32Number intent, in_type, out_type, nr_samples; int *r, *g, *b; int prec, i, max, max_w, max_h; OPJ_COLOR_SPACE oldspace; in_prof = cmsOpenProfileFromMem(image->icc_profile_buf, image->icc_profile_len); #ifdef DEBUG_PROFILE FILE *icm = fopen("debug.icm","wb"); fwrite( image->icc_profile_buf,1, image->icc_profile_len,icm); fclose(icm); #endif if(in_prof == NULL) return; in_space = cmsGetPCS(in_prof); out_space = cmsGetColorSpace(in_prof); intent = cmsGetHeaderRenderingIntent(in_prof); max_w = (int)image->comps[0].w; max_h = (int)image->comps[0].h; prec = (int)image->comps[0].prec; oldspace = image->color_space; if(out_space == cmsSigRgbData) /* enumCS 16 */ { if( prec <= 8 ) { in_type = TYPE_RGB_8; out_type = TYPE_RGB_8; } else { in_type = TYPE_RGB_16; out_type = TYPE_RGB_16; } out_prof = cmsCreate_sRGBProfile(); image->color_space = OPJ_CLRSPC_SRGB; } else if(out_space == cmsSigGrayData) /* enumCS 17 */ { in_type = TYPE_GRAY_8; out_type = TYPE_RGB_8; out_prof = cmsCreate_sRGBProfile(); image->color_space = OPJ_CLRSPC_SRGB; } else if(out_space == cmsSigYCbCrData) /* enumCS 18 */ { in_type = TYPE_YCbCr_16; out_type = TYPE_RGB_16; out_prof = cmsCreate_sRGBProfile(); image->color_space = OPJ_CLRSPC_SRGB; } else { #ifdef DEBUG_PROFILE fprintf(stderr,"%s:%d: color_apply_icc_profile\n\tICC Profile has unknown " "output colorspace(%#x)(%c%c%c%c)\n\tICC Profile ignored.\n", __FILE__,__LINE__,out_space, (out_space>>24) & 0xff,(out_space>>16) & 0xff, (out_space>>8) & 0xff, out_space & 0xff); #endif return; } #ifdef DEBUG_PROFILE fprintf(stderr,"%s:%d:color_apply_icc_profile\n\tchannels(%d) prec(%d) w(%d) h(%d)" "\n\tprofile: in(%p) out(%p)\n",__FILE__,__LINE__,image->numcomps,prec, max_w,max_h, (void*)in_prof,(void*)out_prof); fprintf(stderr,"\trender_intent (%u)\n\t" "color_space: in(%#x)(%c%c%c%c) out:(%#x)(%c%c%c%c)\n\t" " type: in(%u) out:(%u)\n", intent, in_space, (in_space>>24) & 0xff,(in_space>>16) & 0xff, (in_space>>8) & 0xff, in_space & 0xff, out_space, (out_space>>24) & 0xff,(out_space>>16) & 0xff, (out_space>>8) & 0xff, out_space & 0xff, in_type,out_type ); #else (void)prec; (void)in_space; #endif /* DEBUG_PROFILE */ transform = cmsCreateTransform(in_prof, in_type, out_prof, out_type, intent, 0); #ifdef OPJ_HAVE_LIBLCMS2 /* Possible for: LCMS_VERSION >= 2000 :*/ cmsCloseProfile(in_prof); cmsCloseProfile(out_prof); #endif if(transform == NULL) { #ifdef DEBUG_PROFILE fprintf(stderr,"%s:%d:color_apply_icc_profile\n\tcmsCreateTransform failed. " "ICC Profile ignored.\n",__FILE__,__LINE__); #endif image->color_space = oldspace; #ifdef OPJ_HAVE_LIBLCMS1 cmsCloseProfile(in_prof); cmsCloseProfile(out_prof); #endif return; } if(image->numcomps > 2)/* RGB, RGBA */ { if( prec <= 8 ) { unsigned char *inbuf, *outbuf, *in, *out; max = max_w * max_h; nr_samples = (cmsUInt32Number)max * 3 * (cmsUInt32Number)sizeof(unsigned char); in = inbuf = (unsigned char*)malloc(nr_samples); out = outbuf = (unsigned char*)malloc(nr_samples); r = image->comps[0].data; g = image->comps[1].data; b = image->comps[2].data; for(i = 0; i < max; ++i) { *in++ = (unsigned char)*r++; *in++ = (unsigned char)*g++; *in++ = (unsigned char)*b++; } cmsDoTransform(transform, inbuf, outbuf, (cmsUInt32Number)max); r = image->comps[0].data; g = image->comps[1].data; b = image->comps[2].data; for(i = 0; i < max; ++i) { *r++ = (int)*out++; *g++ = (int)*out++; *b++ = (int)*out++; } free(inbuf); free(outbuf); } else { unsigned short *inbuf, *outbuf, *in, *out; max = max_w * max_h; nr_samples = (cmsUInt32Number)max * 3 * (cmsUInt32Number)sizeof(unsigned short); in = inbuf = (unsigned short*)malloc(nr_samples); out = outbuf = (unsigned short*)malloc(nr_samples); r = image->comps[0].data; g = image->comps[1].data; b = image->comps[2].data; for(i = 0; i < max; ++i) { *in++ = (unsigned short)*r++; *in++ = (unsigned short)*g++; *in++ = (unsigned short)*b++; } cmsDoTransform(transform, inbuf, outbuf, (cmsUInt32Number)max); r = image->comps[0].data; g = image->comps[1].data; b = image->comps[2].data; for(i = 0; i < max; ++i) { *r++ = (int)*out++; *g++ = (int)*out++; *b++ = (int)*out++; } free(inbuf); free(outbuf); } } else /* GRAY, GRAYA */ { unsigned char *in, *inbuf, *out, *outbuf; max = max_w * max_h; nr_samples = (cmsUInt32Number)max * 3 * sizeof(unsigned char); in = inbuf = (unsigned char*)malloc(nr_samples); out = outbuf = (unsigned char*)malloc(nr_samples); image->comps = (opj_image_comp_t*) realloc(image->comps, (image->numcomps+2)*sizeof(opj_image_comp_t)); if(image->numcomps == 2) image->comps[3] = image->comps[1]; image->comps[1] = image->comps[0]; image->comps[2] = image->comps[0]; image->comps[1].data = (int*)calloc((size_t)max, sizeof(int)); image->comps[2].data = (int*)calloc((size_t)max, sizeof(int)); image->numcomps += 2; r = image->comps[0].data; for(i = 0; i < max; ++i) { *in++ = (unsigned char)*r++; } cmsDoTransform(transform, inbuf, outbuf, (cmsUInt32Number)max); r = image->comps[0].data; g = image->comps[1].data; b = image->comps[2].data; for(i = 0; i < max; ++i) { *r++ = (int)*out++; *g++ = (int)*out++; *b++ = (int)*out++; } free(inbuf); free(outbuf); }/* if(image->numcomps */ cmsDeleteTransform(transform); #ifdef OPJ_HAVE_LIBLCMS1 cmsCloseProfile(in_prof); cmsCloseProfile(out_prof); #endif }/* color_apply_icc_profile() */
/* Get the link from the CMS, but include proofing and/or a device link profile. Note also, that the source may be a device link profile, in which case we will not have a destination profile but could still have a proof profile or an additional device link profile */ gcmmhlink_t gscms_get_link_proof_devlink(gcmmhprofile_t lcms_srchandle, gcmmhprofile_t lcms_proofhandle, gcmmhprofile_t lcms_deshandle, gcmmhprofile_t lcms_devlinkhandle, gsicc_rendering_param_t *rendering_params, bool src_dev_link, int cmm_flags, gs_memory_t *memory) { cmsUInt32Number src_data_type,des_data_type; cmsColorSpaceSignature src_color_space,des_color_space; int src_nChannels,des_nChannels; int lcms_src_color_space, lcms_des_color_space; cmsHPROFILE hProfiles[5]; int nProfiles = 0; unsigned int flag; /* Check if the rendering intent is something other than relative colorimetric and if we have a proofing profile. In this case we need to create the combined profile a bit different. LCMS does not allow us to use different intents in the cmsCreateMultiprofileTransform transform. Also, don't even think about doing this if someone has snuck in a source based device link profile into the mix */ if (lcms_proofhandle != NULL && rendering_params->rendering_intent != gsRELATIVECOLORIMETRIC && !src_dev_link) { /* First handle the source to proof profile with its particular intent as a device link profile */ cmsHPROFILE src_to_proof; cmsHTRANSFORM temptransform; temptransform = gscms_get_link(lcms_srchandle, lcms_proofhandle, rendering_params, cmm_flags, memory); /* Now mash that to a device link profile */ flag = cmsFLAGS_HIGHRESPRECALC; if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); } src_to_proof = cmsTransform2DeviceLink(temptransform, 3.4, flag); /* Free up the link handle */ cmsDeleteTransform(temptransform); src_color_space = cmsGetColorSpace(src_to_proof); lcms_src_color_space = _cmsLCMScolorSpace(src_color_space); /* littlecms returns -1 for types it does not (but should) understand */ if (lcms_src_color_space < 0) lcms_src_color_space = 0; src_nChannels = cmsChannelsOf(src_color_space); /* For now, just do single byte data, interleaved. We can change this when we use the transformation. */ src_data_type = (COLORSPACE_SH(lcms_src_color_space)| CHANNELS_SH(src_nChannels)|BYTES_SH(2)); if (lcms_devlinkhandle == NULL) { des_color_space = cmsGetColorSpace(lcms_deshandle); } else { des_color_space = cmsGetPCS(lcms_devlinkhandle); } lcms_des_color_space = _cmsLCMScolorSpace(des_color_space); if (lcms_des_color_space < 0) lcms_des_color_space = 0; des_nChannels = cmsChannelsOf(des_color_space); des_data_type = (COLORSPACE_SH(lcms_des_color_space)| CHANNELS_SH(des_nChannels)|BYTES_SH(2)); /* Now, we need to go back through the proofing profile, to the destination and then to the device link profile if there was one. */ hProfiles[nProfiles++] = src_to_proof; /* Src to proof with special intent */ hProfiles[nProfiles++] = lcms_proofhandle; /* Proof to CIELAB */ if (lcms_deshandle != NULL) { hProfiles[nProfiles++] = lcms_deshandle; /* Our destination */ } /* The output device link profile */ if (lcms_devlinkhandle != NULL) { hProfiles[nProfiles++] = lcms_devlinkhandle; } flag = cmsFLAGS_HIGHRESPRECALC; if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); } /* Use relative colorimetric here */ temptransform = cmsCreateMultiprofileTransformTHR((cmsContext)memory, hProfiles, nProfiles, src_data_type, des_data_type, gsRELATIVECOLORIMETRIC, flag); cmsCloseProfile(src_to_proof); return temptransform; } else { /* First handle all the source stuff */ src_color_space = cmsGetColorSpace(lcms_srchandle); lcms_src_color_space = _cmsLCMScolorSpace(src_color_space); /* littlecms returns -1 for types it does not (but should) understand */ if (lcms_src_color_space < 0) lcms_src_color_space = 0; src_nChannels = cmsChannelsOf(src_color_space); /* For now, just do single byte data, interleaved. We can change this when we use the transformation. */ src_data_type = (COLORSPACE_SH(lcms_src_color_space)| CHANNELS_SH(src_nChannels)|BYTES_SH(2)); if (lcms_devlinkhandle == NULL) { if (src_dev_link) { des_color_space = cmsGetPCS(lcms_srchandle); } else { des_color_space = cmsGetColorSpace(lcms_deshandle); } } else { des_color_space = cmsGetPCS(lcms_devlinkhandle); } lcms_des_color_space = _cmsLCMScolorSpace(des_color_space); if (lcms_des_color_space < 0) lcms_des_color_space = 0; des_nChannels = cmsChannelsOf(des_color_space); des_data_type = (COLORSPACE_SH(lcms_des_color_space)| CHANNELS_SH(des_nChannels)|BYTES_SH(2)); /* lcms proofing transform has a clunky API and can't include the device link profile if we have both. So use cmsCreateMultiprofileTransform instead and round trip the proofing profile. */ hProfiles[nProfiles++] = lcms_srchandle; /* Note if source is device link, we cannot do any proofing */ if (lcms_proofhandle != NULL && !src_dev_link) { hProfiles[nProfiles++] = lcms_proofhandle; hProfiles[nProfiles++] = lcms_proofhandle; } /* This should be NULL if we have a source device link */ if (lcms_deshandle != NULL) { hProfiles[nProfiles++] = lcms_deshandle; } /* Someone could have a device link at the output, giving us possibly two device link profiles to smash together */ if (lcms_devlinkhandle != NULL) { hProfiles[nProfiles++] = lcms_devlinkhandle; } flag = cmsFLAGS_HIGHRESPRECALC; if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); } return cmsCreateMultiprofileTransformTHR((cmsContext)memory, hProfiles, nProfiles, src_data_type, des_data_type, rendering_params->rendering_intent, flag); } }
// Does create a Color Space Array on XYZ colorspace for PostScript usage static cmsUInt32Number GenerateCSA(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, cmsIOHANDLER* mem) { cmsUInt32Number dwBytesUsed; cmsPipeline* lut = NULL; cmsStage* Matrix, *Shaper; // Is a named color profile? if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error; } else { // Any profile class are allowed (including devicelink), but // output (PCS) colorspace must be XYZ or Lab cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); if (ColorSpace != cmsSigXYZData && ColorSpace != cmsSigLabData) { cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space"); goto Error; } // Read the lut with all necessary conversion stages lut = _cmsReadInputLUT(hProfile, Intent); if (lut == NULL) goto Error; // Tone curves + matrix can be implemented without any LUT if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) { if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error; } else { // We need a LUT for the rest if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error; } } // Done, keep memory usage dwBytesUsed = mem ->UsedSpace; // Get rid of LUT if (lut != NULL) cmsPipelineFree(lut); // Finally, return used byte count return dwBytesUsed; Error: if (lut != NULL) cmsPipelineFree(lut); return 0; }
icColorSpaceSignature dkCmsGetPCS(cmsHPROFILE hProfile) { return static_cast<icColorSpaceSignature>( cmsGetPCS(hProfile) ); }