static cmsUInt32Number determine_lcms_format (const Babl *babl, cmsHPROFILE profile) { cmsUInt32Number format = COLORSPACE_SH (PT_ANY); gint channels, bpc, alpha; const Babl *type; channels = cmsChannelsOf (cmsGetColorSpace (profile)); alpha = babl_format_get_n_components (babl) - channels; bpc = babl_format_get_bytes_per_pixel (babl) / babl_format_get_n_components (babl); type = babl_format_get_type (babl, 0); if (type == babl_type ("half") || type == babl_type ("float") || type == babl_type ("double")) format |= FLOAT_SH (1); /* bpc == 8 overflows the bitfield otherwise */ bpc &= 0x07; /* * This is needed so the alpha component lines up with RGBA float * for our memcpy hack later on. */ if (alpha > 1 || (alpha && channels != 3)) return 0; format |= EXTRA_SH (alpha) | CHANNELS_SH (channels) | BYTES_SH (bpc); return format; }
/* Get the number of output channels for the profile */ int gscms_get_output_channel_count(gcmmhprofile_t profile) { cmsColorSpaceSignature colorspace; colorspace = cmsGetPCS(profile); return(cmsChannelsOf(colorspace)); }
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; }
void IccColorProfile::calculateFloatUIMinMax(void) { QVector<KoChannelInfo::DoubleRange> &ret = d->shared->uiMinMaxes; cmsHPROFILE cprofile = d->shared->lcmsProfile->lcmsProfile(); Q_ASSERT(cprofile); cmsColorSpaceSignature color_space_sig = cmsGetColorSpace(cprofile); unsigned int num_channels = cmsChannelsOf(color_space_sig); unsigned int color_space_mask = _cmsLCMScolorSpace(color_space_sig); Q_ASSERT(num_channels>=1 && num_channels <=4); // num_channels==1 is for grayscale, we need to handle it Q_ASSERT(color_space_mask); // to try to find the max range of float/doubles for this profile, // pass in min/max int and make the profile convert that // this is far from perfect, we need a better way, if possible to get the "bounds" of a profile uint16_t in_min_pixel[4] = {0,0,0,0}; uint16_t in_max_pixel[4] = {0xFFFF,0xFFFF,0xFFFF,0xFFFF}; double out_min_pixel[4] = {0,0,0,0}; double out_max_pixel[4] = {0,0,0,0}; cmsHTRANSFORM trans = cmsCreateTransform( cprofile, (COLORSPACE_SH(color_space_mask)|CHANNELS_SH(num_channels)|BYTES_SH(2)), cprofile, (COLORSPACE_SH(color_space_mask)|FLOAT_SH(1)|CHANNELS_SH(num_channels)|BYTES_SH(0)), //NOTE THAT 'BYTES' FIELD IS SET TO ZERO ON DLB because 8 bytes overflows the bitfield INTENT_PERCEPTUAL, 0); // does the intent matter in this case? if (trans) { cmsDoTransform(trans, in_min_pixel, out_min_pixel, 1); cmsDoTransform(trans, in_max_pixel, out_max_pixel, 1); cmsDeleteTransform(trans); }//else, we'll just default to [0..1] below ret.resize(num_channels); for (unsigned int i=0; i<num_channels; ++i) { if (out_min_pixel[i] < out_max_pixel[i]) { ret[i].minVal = out_min_pixel[i]; ret[i].maxVal = out_max_pixel[i]; } else { // aparently we can't even guarentee that converted_to_double(0x0000) < converted_to_double(0xFFFF) // assume [0..1] in such cases // we need to find a really solid way of determining the bounds of a profile, if possible ret[i].minVal = 0; ret[i].maxVal = 1; } } }
// This function creates a named color profile dumping all the contents of transform to a single profile // In this way, LittleCMS may be used to "group" several named color databases into a single profile. // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this // is the normal PCS for named color profiles. static cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) { _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; cmsHPROFILE hICC = NULL; int i, nColors; cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL; // Create an empty placeholder hICC = cmsCreateProfilePlaceholder(v->ContextID); if (hICC == NULL) return NULL; // Critical information cmsSetDeviceClass(hICC, cmsSigNamedColorClass); cmsSetColorSpace(hICC, v ->ExitColorSpace); cmsSetPCS(hICC, cmsSigLabData); // Tag profile with information if (!SetTextTags(hICC, L"Named color devicelink")) goto Error; Original = cmsGetNamedColorList(xform); if (Original == NULL) goto Error; nColors = cmsNamedColorCount(Original); nc2 = cmsDupNamedColorList(Original); if (nc2 == NULL) goto Error; // Colorant count now depends on the output space nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut); // Make sure we have proper formatters cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX, FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace)) | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace))); // Apply the transfor to colorants. for (i=0; i < nColors; i++) { cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); } if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error; cmsFreeNamedColorList(nc2); return hICC; Error: if (hICC != NULL) cmsCloseProfile(hICC); return NULL; }
static void PrintEncodedResults(cmsUInt16Number Encoded[]) { cmsUInt32Number i, n; char ChannelName[cmsMAX_PATH]; cmsUInt32Number v; n = cmsChannelsOf(OutputColorSpace); for (i=0; i < n; i++) { if (OutputColorant != NULL) { cmsNamedColorInfo(OutputColorant, i, ChannelName, NULL, NULL, NULL, NULL); } else { sprintf(ChannelName, "Channel #%u", i + 1); } if (Verbose > 0) printf("%s=", ChannelName); v = Encoded[i]; if (InHexa) { if (Width16) printf("0x%04X ", (int) floor(v + .5)); else printf("0x%02X ", (int) floor(v / 257. + .5)); } else { if (Width16) printf("%d ", (int) floor(v + .5)); else printf("%d ", (int) floor(v / 257. + .5)); } } printf("\n"); }
// Print a value which is given in double floating point static void PrintFloatResults(cmsFloat64Number Value[]) { cmsUInt32Number i, n; char ChannelName[cmsMAX_PATH]; cmsFloat64Number v; n = cmsChannelsOf(OutputColorSpace); for (i=0; i < n; i++) { if (OutputColorant != NULL) { cmsNamedColorInfo(OutputColorant, i, ChannelName, NULL, NULL, NULL, NULL); } else { OutputRange = 1; sprintf(ChannelName, "Channel #%u", i + 1); } v = (cmsFloat64Number) Value[i]* OutputRange; if (lQuantize) v = floor(v + 0.5); if (!lUnbounded) { if (v < 0) v = 0; if (v > OutputRange) v = OutputRange; } if (Verbose <= 0) printf("%.4f ", v); else printf("%s=%.4f ", ChannelName, v); } printf("\n"); }
// Read values from a text file or terminal static void TakeFloatValues(cmsFloat64Number Float[]) { cmsUInt32Number i, n; char ChannelName[cmsMAX_PATH]; char Buffer[4096]; if (xisatty(stdin)) fprintf(stderr, "\nEnter values, 'q' to quit\n"); if (InputNamedColor) { // This is named color index, which is always cmsUInt16Number cmsUInt16Number index = GetIndex(); memcpy(Float, &index, sizeof(cmsUInt16Number)); return; } n = cmsChannelsOf(InputColorSpace); for (i=0; i < n; i++) { if (InputColorant) { cmsNamedColorInfo(InputColorant, i, ChannelName, NULL, NULL, NULL, NULL); } else { InputRange = 1; sprintf(ChannelName, "Channel #%u", i+1); } GetLine(Buffer, "%s? ", ChannelName); Float[i] = (cmsFloat64Number) atof(Buffer) / InputRange; } if (xisatty(stdin)) fprintf(stderr, "\n"); }
static void TakeCGATSValues(int nPatch, cmsFloat64Number Float[]) { // At first take the name if SAMPLE_ID is present if (cmsIT8GetPatchName(hIT8in, nPatch, CGATSPatch) == NULL) { FatalError("Sorry, I need 'SAMPLE_ID' on input CGATS to operate."); } // Special handling for named color profiles. // Lookup the name in the names database (the transform) if (InputNamedColor) { const cmsNAMEDCOLORLIST* NamedColorList; int index; NamedColorList = cmsGetNamedColorList(hTrans); if (NamedColorList == NULL) FatalError("Malformed named color profile"); index = cmsNamedColorIndex(NamedColorList, CGATSPatch); if (index < 0) FatalError("Named color '%s' not found in the profile", CGATSPatch); Float[0] = index; return; } // Color is not a spot color, proceed. switch (InputColorSpace) { // Encoding should follow CGATS specification. case cmsSigXYZData: Float[0] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "XYZ_X") / 100.0; Float[1] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "XYZ_Y") / 100.0; Float[2] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "XYZ_Z") / 100.0; break; case cmsSigLabData: Float[0] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "LAB_L"); Float[1] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "LAB_A"); Float[2] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "LAB_B"); break; case cmsSigRgbData: Float[0] = GetIT8Val("RGB_R", 255.0); Float[1] = GetIT8Val("RGB_G", 255.0); Float[2] = GetIT8Val("RGB_B", 255.0); break; case cmsSigGrayData: Float[0] = GetIT8Val("GRAY", 255.0); break; case cmsSigCmykData: Float[0] = GetIT8Val("CMYK_C", 1.0); Float[1] = GetIT8Val("CMYK_M", 1.0); Float[2] = GetIT8Val("CMYK_Y", 1.0); Float[3] = GetIT8Val("CMYK_K", 1.0); break; case cmsSigCmyData: Float[0] = GetIT8Val("CMY_C", 1.0); Float[1] = GetIT8Val("CMY_M", 1.0); Float[2] = GetIT8Val("CMY_Y", 1.0); break; case cmsSig1colorData: case cmsSig2colorData: case cmsSig3colorData: case cmsSig4colorData: case cmsSig5colorData: case cmsSig6colorData: case cmsSig7colorData: case cmsSig8colorData: case cmsSig9colorData: case cmsSig10colorData: case cmsSig11colorData: case cmsSig12colorData: case cmsSig13colorData: case cmsSig14colorData: case cmsSig15colorData: { cmsUInt32Number i, n; n = cmsChannelsOf(InputColorSpace); for (i=0; i < n; i++) { char Buffer[255]; sprintf(Buffer, "%uCLR_%u", n, i+1); Float[i] = GetIT8Val(Buffer, 100.0); } } break; default: { cmsUInt32Number i, n; n = cmsChannelsOf(InputColorSpace); for (i=0; i < n; i++) { char Buffer[255]; sprintf(Buffer, "CHAN_%u", i+1); Float[i] = GetIT8Val(Buffer, 1.0); } } } }
// Populate a named color list with usual component names. // I am using the first Colorant channel to store the range, but it works since // this space is not used anyway. static cmsNAMEDCOLORLIST* ComponentNames(cmsColorSpaceSignature space, cmsBool IsInput) { cmsNAMEDCOLORLIST* out; int i, n; char Buffer[cmsMAX_PATH]; out = cmsAllocNamedColorList(0, 12, cmsMAXCHANNELS, "", ""); if (out == NULL) return NULL; switch (space) { case cmsSigXYZData: SetRange(100, IsInput); cmsAppendNamedColor(out, "X", NULL, NULL); cmsAppendNamedColor(out, "Y", NULL, NULL); cmsAppendNamedColor(out, "Z", NULL, NULL); break; case cmsSigLabData: SetRange(1, IsInput); cmsAppendNamedColor(out, "L*", NULL, NULL); cmsAppendNamedColor(out, "a*", NULL, NULL); cmsAppendNamedColor(out, "b*", NULL, NULL); break; case cmsSigLuvData: SetRange(1, IsInput); cmsAppendNamedColor(out, "L", NULL, NULL); cmsAppendNamedColor(out, "u", NULL, NULL); cmsAppendNamedColor(out, "v", NULL, NULL); break; case cmsSigYCbCrData: SetRange(255, IsInput); cmsAppendNamedColor(out, "Y", NULL, NULL ); cmsAppendNamedColor(out, "Cb", NULL, NULL); cmsAppendNamedColor(out, "Cr", NULL, NULL); break; case cmsSigYxyData: SetRange(1, IsInput); cmsAppendNamedColor(out, "Y", NULL, NULL); cmsAppendNamedColor(out, "x", NULL, NULL); cmsAppendNamedColor(out, "y", NULL, NULL); break; case cmsSigRgbData: SetRange(255, IsInput); cmsAppendNamedColor(out, "R", NULL, NULL); cmsAppendNamedColor(out, "G", NULL, NULL); cmsAppendNamedColor(out, "B", NULL, NULL); break; case cmsSigGrayData: SetRange(255, IsInput); cmsAppendNamedColor(out, "G", NULL, NULL); break; case cmsSigHsvData: SetRange(255, IsInput); cmsAppendNamedColor(out, "H", NULL, NULL); cmsAppendNamedColor(out, "s", NULL, NULL); cmsAppendNamedColor(out, "v", NULL, NULL); break; case cmsSigHlsData: SetRange(255, IsInput); cmsAppendNamedColor(out, "H", NULL, NULL); cmsAppendNamedColor(out, "l", NULL, NULL); cmsAppendNamedColor(out, "s", NULL, NULL); break; case cmsSigCmykData: SetRange(1, IsInput); cmsAppendNamedColor(out, "C", NULL, NULL); cmsAppendNamedColor(out, "M", NULL, NULL); cmsAppendNamedColor(out, "Y", NULL, NULL); cmsAppendNamedColor(out, "K", NULL, NULL); break; case cmsSigCmyData: SetRange(1, IsInput); cmsAppendNamedColor(out, "C", NULL, NULL); cmsAppendNamedColor(out, "M", NULL, NULL); cmsAppendNamedColor(out, "Y", NULL, NULL); break; default: SetRange(1, IsInput); n = cmsChannelsOf(space); for (i=0; i < n; i++) { sprintf(Buffer, "Channel #%d", i + 1); cmsAppendNamedColor(out, Buffer, NULL, NULL); } } return out; }
// New to lcms 2.0 -- have all parameters available. cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsBool BPC[], cmsUInt32Number Intents[], cmsFloat64Number AdaptationStates[], cmsHPROFILE hGamutProfile, cmsUInt32Number nGamutPCSposition, cmsUInt32Number InputFormat, cmsUInt32Number OutputFormat, cmsUInt32Number dwFlags) { _cmsTRANSFORM* xform; cmsColorSpaceSignature EntryColorSpace; cmsColorSpaceSignature ExitColorSpace; cmsPipeline* Lut; cmsUInt32Number LastIntent = Intents[nProfiles-1]; // If it is a fake transform if (dwFlags & cmsFLAGS_NULLTRANSFORM) { return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags); } // If gamut check is requested, make sure we have a gamut profile if (dwFlags & cmsFLAGS_GAMUTCHECK) { if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK; } // On floating point transforms, inhibit cache if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) dwFlags |= cmsFLAGS_NOCACHE; // Mark entry/exit spaces if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) { cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform"); return NULL; } // Check if proper colorspaces if (!IsProperColorSpace(EntryColorSpace, InputFormat)) { cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform"); return NULL; } if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) { cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform"); return NULL; } // Create a pipeline with all transformations Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); if (Lut == NULL) { cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles"); return NULL; } // Check channel count if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) || (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) { cmsPipelineFree(Lut); cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted"); return NULL; } // All seems ok xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags); if (xform == NULL) { return NULL; } // Keep values xform ->EntryColorSpace = EntryColorSpace; xform ->ExitColorSpace = ExitColorSpace; xform ->RenderingIntent = Intents[nProfiles-1]; // Take white points SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag)); SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag)); // Create a gamut check LUT if requested if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK)) xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, BPC, Intents, AdaptationStates, nGamutPCSposition, hGamutProfile); // Try to read input and output colorant table if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) { // Input table can only come in this way. xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag)); } // Output is a little bit more complex. if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) { // This tag may exist only on devicelink profiles. if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) { // It may be NULL if error xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)); } } else { if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) { xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)); } } // Store the sequence of profiles if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) { xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles); } else xform ->Sequence = NULL; // If this is a cached transform, init first value, which is zero (16 bits only) if (!(dwFlags & cmsFLAGS_NOCACHE)) { memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn)); if (xform ->GamutCheck != NULL) { TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut); } else { xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data); } } return (cmsHTRANSFORM) xform; }
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; }
/* 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); } }
/* 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*/ }
static void PutCGATSValues(cmsFloat64Number Float[]) { cmsIT8SetData(hIT8out, CGATSPatch, "SAMPLE_ID", CGATSPatch); switch (OutputColorSpace) { // Encoding should follow CGATS specification. case cmsSigXYZData: SetCGATSfld("XYZ_X", Float[0] * 100.0); SetCGATSfld("XYZ_Y", Float[1] * 100.0); SetCGATSfld("XYZ_Z", Float[2] * 100.0); break; case cmsSigLabData: SetCGATSfld("LAB_L", Float[0]); SetCGATSfld("LAB_A", Float[1]); SetCGATSfld("LAB_B", Float[2]); break; case cmsSigRgbData: SetCGATSfld("RGB_R", Float[0] * 255.0); SetCGATSfld("RGB_G", Float[1] * 255.0); SetCGATSfld("RGB_B", Float[2] * 255.0); break; case cmsSigGrayData: SetCGATSfld("GRAY", Float[0] * 255.0); break; case cmsSigCmykData: SetCGATSfld("CMYK_C", Float[0]); SetCGATSfld("CMYK_M", Float[1]); SetCGATSfld("CMYK_Y", Float[2]); SetCGATSfld("CMYK_K", Float[3]); break; case cmsSigCmyData: SetCGATSfld("CMY_C", Float[0]); SetCGATSfld("CMY_M", Float[1]); SetCGATSfld("CMY_Y", Float[2]); break; case cmsSig1colorData: case cmsSig2colorData: case cmsSig3colorData: case cmsSig4colorData: case cmsSig5colorData: case cmsSig6colorData: case cmsSig7colorData: case cmsSig8colorData: case cmsSig9colorData: case cmsSig10colorData: case cmsSig11colorData: case cmsSig12colorData: case cmsSig13colorData: case cmsSig14colorData: case cmsSig15colorData: { cmsUInt32Number i, n; n = cmsChannelsOf(InputColorSpace); for (i=0; i < n; i++) { char Buffer[255]; sprintf(Buffer, "%uCLR_%u", n, i+1); SetCGATSfld(Buffer, Float[i] * 100.0); } } break; default: { cmsUInt32Number i, n; n = cmsChannelsOf(InputColorSpace); for (i=0; i < n; i++) { char Buffer[255]; sprintf(Buffer, "CHAN_%u", i+1); SetCGATSfld(Buffer, Float[i]); } } } }
// 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 = cmsSigLabData, 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; } // Check for non-negatives clip if (dwFlags & cmsFLAGS_NONEGATIVES) { if (ColorSpaceOut == cmsSigGrayData || ColorSpaceOut == cmsSigRgbData || ColorSpaceOut == cmsSigCmykData) { cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOf(ColorSpaceOut)); if (clip == NULL) goto Error; if (!cmsPipelineInsertStage(Result, cmsAT_END, clip)) goto Error; } } return Result; Error: if (Lut != NULL) cmsPipelineFree(Lut); if (Result != NULL) cmsPipelineFree(Result); return NULL; cmsUNUSED_PARAMETER(dwFlags); }
// Create data format static void SetOutputDataFormat(void) { cmsIT8DefineDblFormat(hIT8out, "%.4g"); cmsIT8SetPropertyStr(hIT8out, "ORIGINATOR", "icctrans"); if (IncludePart != NULL) cmsIT8SetPropertyStr(hIT8out, ".INCLUDE", IncludePart); cmsIT8SetComment(hIT8out, "Data follows"); cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_SETS", nMaxPatches); switch (OutputColorSpace) { // Encoding should follow CGATS specification. case cmsSigXYZData: cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4); cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID"); cmsIT8SetDataFormat(hIT8out, 1, "XYZ_X"); cmsIT8SetDataFormat(hIT8out, 2, "XYZ_Y"); cmsIT8SetDataFormat(hIT8out, 3, "XYZ_Z"); break; case cmsSigLabData: cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4); cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID"); cmsIT8SetDataFormat(hIT8out, 1, "LAB_L"); cmsIT8SetDataFormat(hIT8out, 2, "LAB_A"); cmsIT8SetDataFormat(hIT8out, 3, "LAB_B"); break; case cmsSigRgbData: cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4); cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID"); cmsIT8SetDataFormat(hIT8out, 1, "RGB_R"); cmsIT8SetDataFormat(hIT8out, 2, "RGB_G"); cmsIT8SetDataFormat(hIT8out, 3, "RGB_B"); break; case cmsSigGrayData: cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 2); cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID"); cmsIT8SetDataFormat(hIT8out, 1, "GRAY"); break; case cmsSigCmykData: cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 5); cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID"); cmsIT8SetDataFormat(hIT8out, 1, "CMYK_C"); cmsIT8SetDataFormat(hIT8out, 2, "CMYK_M"); cmsIT8SetDataFormat(hIT8out, 3, "CMYK_Y"); cmsIT8SetDataFormat(hIT8out, 4, "CMYK_K"); break; case cmsSigCmyData: cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4); cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID"); cmsIT8SetDataFormat(hIT8out, 1, "CMY_C"); cmsIT8SetDataFormat(hIT8out, 2, "CMY_M"); cmsIT8SetDataFormat(hIT8out, 3, "CMY_Y"); break; case cmsSig1colorData: case cmsSig2colorData: case cmsSig3colorData: case cmsSig4colorData: case cmsSig5colorData: case cmsSig6colorData: case cmsSig7colorData: case cmsSig8colorData: case cmsSig9colorData: case cmsSig10colorData: case cmsSig11colorData: case cmsSig12colorData: case cmsSig13colorData: case cmsSig14colorData: case cmsSig15colorData: { int i, n; char Buffer[255]; n = cmsChannelsOf(OutputColorSpace); cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", n+1); cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID"); for (i=1; i <= n; i++) { sprintf(Buffer, "%dCLR_%d", n, i); cmsIT8SetDataFormat(hIT8out, i, Buffer); } } break; default: { int i, n; char Buffer[255]; n = cmsChannelsOf(OutputColorSpace); cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", n+1); cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID"); for (i=1; i <= n; i++) { sprintf(Buffer, "CHAN_%d", i); cmsIT8SetDataFormat(hIT8out, i, Buffer); } } } }
cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, cmsHPROFILE hProfiles[], cmsBool BPC[], cmsUInt32Number Intents[], cmsFloat64Number AdaptationStates[], cmsUInt32Number nGamutPCSposition, cmsHPROFILE hGamut) { cmsHPROFILE hLab; cmsPipeline* Gamut; cmsStage* CLUT; cmsUInt32Number dwFormat; GAMUTCHAIN Chain; int nChannels, nGridpoints; cmsColorSpaceSignature ColorSpace; cmsUInt32Number i; cmsHPROFILE ProfileList[256]; cmsBool BPCList[256]; cmsFloat64Number AdaptationList[256]; cmsUInt32Number IntentList[256]; memset(&Chain, 0, sizeof(GAMUTCHAIN)); if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) { cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition); return NULL; } hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); if (hLab == NULL) return NULL; // The figure of merit. On matrix-shaper profiles, should be almost zero as // the conversion is pretty exact. On LUT based profiles, different resolutions // of input and output CLUT may result in differences. if (cmsIsMatrixShaper(hGamut)) { Chain.Thereshold = 1.0; } else { Chain.Thereshold = ERR_THERESHOLD; } // Create a copy of parameters for (i=0; i < nGamutPCSposition; i++) { ProfileList[i] = hProfiles[i]; BPCList[i] = BPC[i]; AdaptationList[i] = AdaptationStates[i]; IntentList[i] = Intents[i]; } // Fill Lab identity ProfileList[nGamutPCSposition] = hLab; BPCList[nGamutPCSposition] = 0; AdaptationList[nGamutPCSposition] = 1.0; IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC; ColorSpace = cmsGetColorSpace(hGamut); nChannels = cmsChannelsOf(ColorSpace); nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); // 16 bits to Lab double Chain.hInput = cmsCreateExtendedTransform(ContextID, nGamutPCSposition + 1, ProfileList, BPCList, IntentList, AdaptationList, NULL, 0, dwFormat, TYPE_Lab_DBL, cmsFLAGS_NOCACHE); // Does create the forward step. Lab double to device dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); Chain.hForward = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_DBL, hGamut, dwFormat, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE); // Does create the backwards step Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE); // All ok? if (Chain.hInput && Chain.hForward && Chain.hReverse) { // Go on, try to compute gamut LUT from PCS. This consist on a single channel containing // dE when doing a transform back and forth on the colorimetric intent. Gamut = cmsPipelineAlloc(ContextID, 3, 1); if (Gamut != NULL) { CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL); if (!cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT)) { cmsPipelineFree(Gamut); Gamut = NULL; } else { cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0); } } } else Gamut = NULL; // Didn't work... // Free all needed stuff. if (Chain.hInput) cmsDeleteTransform(Chain.hInput); if (Chain.hForward) cmsDeleteTransform(Chain.hForward); if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse); if (hLab) cmsCloseProfile(hLab); // And return computed hull return Gamut; }
void ColorICCSelector::_switchToProfile( gchar const* name ) { bool dirty = false; SPColor tmp( _color ); if ( name ) { if ( tmp.icc && tmp.icc->colorProfile == name ) { #ifdef DEBUG_LCMS g_message("Already at name [%s]", name ); #endif // DEBUG_LCMS } else { #ifdef DEBUG_LCMS g_message("Need to switch to profile [%s]", name ); #endif // DEBUG_LCMS if ( tmp.icc ) { tmp.icc->colors.clear(); } else { tmp.icc = new SVGICCColor(); } tmp.icc->colorProfile = name; Inkscape::ColorProfile* newProf = SP_ACTIVE_DOCUMENT->profileManager->find(name); if ( newProf ) { cmsHTRANSFORM trans = newProf->getTransfFromSRGB8(); if ( trans ) { guint32 val = _color.toRGBA32(0); guchar pre[4] = { static_cast<guchar>(SP_RGBA32_R_U(val)), static_cast<guchar>(SP_RGBA32_G_U(val)), static_cast<guchar>(SP_RGBA32_B_U(val)), 255}; #ifdef DEBUG_LCMS g_message("Shoving in [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); #endif // DEBUG_LCMS cmsUInt16Number post[4] = {0,0,0,0}; cmsDoTransform( trans, pre, post, 1 ); #ifdef DEBUG_LCMS g_message("got on out [%04x] [%04x] [%04x] [%04x]", post[0], post[1], post[2], post[3]); #endif // DEBUG_LCMS #if HAVE_LIBLCMS1 guint count = _cmsChannelsOf( asICColorSpaceSig(newProf->getColorSpace()) ); #elif HAVE_LIBLCMS2 guint count = cmsChannelsOf( asICColorSpaceSig(newProf->getColorSpace()) ); #endif gchar const** names = 0; gchar const** tips = 0; guint const* scales = 0; getThings( asICColorSpaceSig(newProf->getColorSpace()), names, tips, scales ); for ( guint i = 0; i < count; i++ ) { gdouble val = (((gdouble)post[i])/65535.0) * (gdouble)scales[i]; #ifdef DEBUG_LCMS g_message(" scaled %d by %d to be %f", i, scales[i], val); #endif // DEBUG_LCMS tmp.icc->colors.push_back(val); } cmsHTRANSFORM retrans = newProf->getTransfToSRGB8(); if ( retrans ) { cmsDoTransform( retrans, post, pre, 1 ); #ifdef DEBUG_LCMS g_message(" back out [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); #endif // DEBUG_LCMS tmp.set(SP_RGBA32_U_COMPOSE(pre[0], pre[1], pre[2], 0xff)); } } } dirty = true; } } else { #ifdef DEBUG_LCMS g_message("NUKE THE ICC"); #endif // DEBUG_LCMS if ( tmp.icc ) { delete tmp.icc; tmp.icc = 0; dirty = true; _fixupHit( 0, this ); } else { #ifdef DEBUG_LCMS g_message("No icc to nuke"); #endif // DEBUG_LCMS } } if ( dirty ) { #ifdef DEBUG_LCMS g_message("+----------------"); g_message("+ new color is [%s]", tmp.toString().c_str()); #endif // DEBUG_LCMS _setProfile( tmp.icc ); //_adjustmentChanged( _fooAdj[0], SP_COLOR_ICC_SELECTOR(_csel) ); setColorAlpha( tmp, _alpha, true ); #ifdef DEBUG_LCMS g_message("+_________________"); #endif // DEBUG_LCMS } }