// The default error logger does nothing. static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text) { // fprintf(stderr, "[lcms]: %s\n", Text); // fflush(stderr); cmsUNUSED_PARAMETER(ContextID); cmsUNUSED_PARAMETER(ErrorCode); cmsUNUSED_PARAMETER(Text); }
// This callback just accounts the maximum ink dropped in the given node. It does not populate any // memory, as the destination table is NULL. Its only purpose it to know the global maximum. static int EstimateTAC(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo) { cmsTACestimator* bp = (cmsTACestimator*) Cargo; cmsFloat32Number RoundTrip[cmsMAXCHANNELS]; cmsUInt32Number i; cmsFloat32Number Sum; // Evaluate the xform cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1); // All all amounts of ink for (Sum=0, i=0; i < bp ->nOutputChans; i++) Sum += RoundTrip[i]; // If above maximum, keep track of input values if (Sum > bp ->MaxTAC) { bp ->MaxTAC = Sum; for (i=0; i < bp ->nOutputChans; i++) { bp ->MaxInput[i] = In[i]; } } return TRUE; cmsUNUSED_PARAMETER(Out); }
// Interpolate missing parts. The algorithm fist computes slices at // theta=0 and theta=Max. cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags) { int alpha, theta; cmsGDB* gbd = (cmsGDB*) hGBD; _cmsAssert(hGBD != NULL); // Interpolate black for (alpha = 0; alpha < SECTORS; alpha++) { if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE; } // Interpolate white for (alpha = 0; alpha < SECTORS; alpha++) { if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE; } // Interpolate Mid for (theta = 1; theta < SECTORS; theta++) { for (alpha = 0; alpha < SECTORS; alpha++) { if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE; } } // Done return TRUE; cmsUNUSED_PARAMETER(dwFlags); }
// This is the default memory allocation function. It does a very coarse // check of amout of memory, just to prevent exploits static void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size) { if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never allow over maximum return (void*) malloc(size); cmsUNUSED_PARAMETER(ContextID); }
// The default realloc function. Again it checks for exploits. If Ptr is NULL, // realloc behaves the same way as malloc and allocates a new block of size bytes. static void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size) { if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never realloc over 512Mb return realloc(Ptr, size); cmsUNUSED_PARAMETER(ContextID); }
// The default free function. The only check proformed is against NULL pointers static void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr) { // free(NULL) is defined a no-op by C99, therefore it is safe to // avoid the check, but it is here just in case... if (Ptr) free(Ptr); cmsUNUSED_PARAMETER(ContextID); }
static cmsUInt32Number GenerateCRD(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, cmsIOHANDLER* mem) { cmsUInt32Number dwBytesUsed; if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile); } // Is a named color profile? if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) { return 0; } } else { // CRD are always implemented as LUT if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) { return 0; } } if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { _cmsIOPrintf(mem, "%%%%EndResource\n"); _cmsIOPrintf(mem, "\n%% CRD End\n"); } // Done, keep memory usage dwBytesUsed = mem ->UsedSpace; // Finally, return used byte count return dwBytesUsed; cmsUNUSED_PARAMETER(ContextID); }
// 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); }
static void defMtxUnlock(cmsContext id, void* mtx) { cmsUNUSED_PARAMETER(id); _cmsUnlockPrimitive((_cmsMutex *) mtx); }
static cmsBool defMtxLock(cmsContext id, void* mtx) { cmsUNUSED_PARAMETER(id); return _cmsLockPrimitive((_cmsMutex *) mtx) == 0; }
// Use darker colorants to obtain black point. This works in the relative colorimetric intent and // assumes more ink results in darker colors. No ink limit is assumed. static cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint, cmsUInt32Number dwFlags) { cmsUInt16Number *Black; cmsHTRANSFORM xform; cmsColorSpaceSignature Space; cmsUInt32Number nChannels; cmsUInt32Number dwFormat; cmsHPROFILE hLab; cmsCIELab Lab; cmsCIEXYZ BlackXYZ; cmsContext ContextID = cmsGetProfileContextID(hInput); // If the profile does not support input direction, assume Black point 0 if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) { BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; return FALSE; } // Create a formatter which has n channels and floating point dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2); // Try to get black by using black colorant Space = cmsGetColorSpace(hInput); // This function returns darker colorant in 16 bits for several spaces if (!_cmsEndPointsBySpace(Space, NULL, &Black, &nChannels)) { BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; return FALSE; } if (nChannels != T_CHANNELS(dwFormat)) { BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; return FALSE; } // Lab will be used as the output space, but lab2 will avoid recursion hLab = cmsCreateLab2ProfileTHR(ContextID, NULL); if (hLab == NULL) { BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; return FALSE; } // Create the transform xform = cmsCreateTransformTHR(ContextID, hInput, dwFormat, hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); cmsCloseProfile(hLab); if (xform == NULL) { // Something went wrong. Get rid of open resources and return zero as black BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; return FALSE; } // Convert black to Lab cmsDoTransform(xform, Black, &Lab, 1); // Force it to be neutral, clip to max. L* of 50 Lab.a = Lab.b = 0; if (Lab.L > 50) Lab.L = 50; // Free the resources cmsDeleteTransform(xform); // Convert from Lab (which is now clipped) to XYZ. cmsLab2XYZ(NULL, &BlackXYZ, &Lab); if (BlackPoint != NULL) *BlackPoint = BlackXYZ; return TRUE; cmsUNUSED_PARAMETER(dwFlags); }