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; }
static cmsUInt32Number ComputeFormatDescriptor (int OutColorSpace, int bps) { int IsPlanar = 0; int Channels = 3; int IsFlt = 0; return (FLOAT_SH(IsFlt)|COLORSPACE_SH(OutColorSpace)|PLANAR_SH(IsPlanar)|CHANNELS_SH(Channels)|BYTES_SH(bps)); }
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; }
// This is the entry for black-plane preserving, which are non-ICC static cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, cmsUInt32Number nProfiles, cmsUInt32Number TheIntents[], cmsHPROFILE hProfiles[], cmsBool BPC[], cmsFloat64Number AdaptationStates[], cmsUInt32Number dwFlags) { PreserveKPlaneParams bp; cmsPipeline* Result = NULL; cmsUInt32Number ICCIntents[256]; cmsStage* CLUT; cmsUInt32Number i, nGridPoints; cmsHPROFILE hLab; // Sanity check if (nProfiles < 1 || nProfiles > 255) return NULL; // Translate black-preserving intents to ICC ones for (i=0; i < nProfiles; i++) ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); // Check for non-cmyk profiles if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || !(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData || cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass)) return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); // Allocate an empty LUT for holding the result Result = cmsPipelineAlloc(ContextID, 4, 4); if (Result == NULL) return NULL; memset(&bp, 0, sizeof(bp)); // We need the input LUT of the last profile, assuming this one is responsible of // black generation. This LUT will be seached in inverse order. bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC); if (bp.LabK2cmyk == NULL) goto Cleanup; // Get total area coverage (in 0..1 domain) bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0; if (bp.MaxTAC <= 0) goto Cleanup; // Create a LUT holding normal ICC transform bp.cmyk2cmyk = DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); if (bp.cmyk2cmyk == NULL) goto Cleanup; // Now the tone curve bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); if (bp.KTone == NULL) goto Cleanup; // To measure the output, Last profile to Lab hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); bp.hProofOutput = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); if ( bp.hProofOutput == NULL) goto Cleanup; // Same as anterior, but lab in the 0..1 range bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4), INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); if (bp.cmyk2Lab == NULL) goto Cleanup; cmsCloseProfile(hLab); // Error estimation (for debug only) bp.MaxError = 0; // How many gridpoints are we going to use? nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); if (CLUT == NULL) goto Cleanup; if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) goto Cleanup; cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); Cleanup: if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk); if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab); if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput); if (bp.KTone) cmsFreeToneCurve(bp.KTone); if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk); return Result; }
static cmsFormattersFloat InputFormattersFloat[] = { // Type Mask Function // ---------------------------- ------------------------------------ ---------------------------- { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, UnrollLabDoubleToFloat}, { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, UnrollLabFloatToFloat}, { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, UnrollXYZDoubleToFloat}, { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatToFloat}, { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| ANYCHANNELS|ANYSPACE, UnrollFloatsToFloat}, { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| ANYCHANNELS|ANYSPACE, UnrollDoublesToFloat}, #ifndef CMS_NO_HALF_SUPPORT { FLOAT_SH(1)|BYTES_SH(2), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| ANYCHANNELS|ANYSPACE, UnrollHalfToFloat}, #endif };