This source file includes following definitions.
- DupPluginIntentsList
- _cmsAllocIntentsPluginChunk
- SearchIntent
- ComputeBlackPointCompensation
- CHAD2Temp
- Temp2CHAD
- ComputeAbsoluteIntent
- IsEmptyLayer
- ComputeConversion
- AddConversion
- ColorSpaceIsCompatible
- DefaultICCintents
- _cmsDefaultICCintents
- TranslateNonICCIntents
- BlackPreservingGrayOnlySampler
- BlackPreservingKOnlyIntents
- BlackPreservingSampler
- BlackPreservingKPlaneIntents
- _cmsLinkProfiles
- cmsGetSupportedIntentsTHR
- cmsGetSupportedIntents
- _cmsRegisterRenderingIntentPlugin
#include "lcms2_internal.h"
cmsPipeline* _cmsLinkProfiles(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number Intents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags);
static
cmsPipeline* DefaultICCintents(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number Intents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags);
static
cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number Intents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags);
static
cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number Intents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags);
typedef struct _cms_intents_list {
cmsUInt32Number Intent;
char Description[256];
cmsIntentFn Link;
struct _cms_intents_list* Next;
} cmsIntentsList;
static cmsIntentsList DefaultIntents[] = {
{ INTENT_PERCEPTUAL, "Perceptual", DefaultICCintents, &DefaultIntents[1] },
{ INTENT_RELATIVE_COLORIMETRIC, "Relative colorimetric", DefaultICCintents, &DefaultIntents[2] },
{ INTENT_SATURATION, "Saturation", DefaultICCintents, &DefaultIntents[3] },
{ INTENT_ABSOLUTE_COLORIMETRIC, "Absolute colorimetric", DefaultICCintents, &DefaultIntents[4] },
{ INTENT_PRESERVE_K_ONLY_PERCEPTUAL, "Perceptual preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[5] },
{ INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC, "Relative colorimetric preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[6] },
{ INTENT_PRESERVE_K_ONLY_SATURATION, "Saturation preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[7] },
{ INTENT_PRESERVE_K_PLANE_PERCEPTUAL, "Perceptual preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[8] },
{ INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC,"Relative colorimetric preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[9] },
{ INTENT_PRESERVE_K_PLANE_SATURATION, "Saturation preserving black plane", BlackPreservingKPlaneIntents, NULL }
};
_cmsIntentsPluginChunkType _cmsIntentsPluginChunk = { NULL };
static
void DupPluginIntentsList(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
_cmsIntentsPluginChunkType newHead = { NULL };
cmsIntentsList* entry;
cmsIntentsList* Anterior = NULL;
_cmsIntentsPluginChunkType* head = (_cmsIntentsPluginChunkType*) src->chunks[IntentPlugin];
for (entry = head->Intents;
entry != NULL;
entry = entry ->Next) {
cmsIntentsList *newEntry = ( cmsIntentsList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsIntentsList));
if (newEntry == NULL)
return;
newEntry -> Next = NULL;
if (Anterior)
Anterior -> Next = newEntry;
Anterior = newEntry;
if (newHead.Intents == NULL)
newHead.Intents = newEntry;
}
ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsIntentsPluginChunkType));
}
void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
if (src != NULL) {
DupPluginIntentsList(ctx, src);
}
else {
static _cmsIntentsPluginChunkType IntentsPluginChunkType = { NULL };
ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx ->MemPool, &IntentsPluginChunkType, sizeof(_cmsIntentsPluginChunkType));
}
}
static
cmsIntentsList* SearchIntent(cmsContext ContextID, cmsUInt32Number Intent)
{
_cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin);
cmsIntentsList* pt;
for (pt = ctx -> Intents; pt != NULL; pt = pt -> Next)
if (pt ->Intent == Intent) return pt;
for (pt = DefaultIntents; pt != NULL; pt = pt -> Next)
if (pt ->Intent == Intent) return pt;
return NULL;
}
static
void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn,
const cmsCIEXYZ* BlackPointOut,
cmsMAT3* m, cmsVEC3* off)
{
cmsFloat64Number ax, ay, az, bx, by, bz, tx, ty, tz;
tx = BlackPointIn->X - cmsD50_XYZ()->X;
ty = BlackPointIn->Y - cmsD50_XYZ()->Y;
tz = BlackPointIn->Z - cmsD50_XYZ()->Z;
ax = (BlackPointOut->X - cmsD50_XYZ()->X) / tx;
ay = (BlackPointOut->Y - cmsD50_XYZ()->Y) / ty;
az = (BlackPointOut->Z - cmsD50_XYZ()->Z) / tz;
bx = - cmsD50_XYZ()-> X * (BlackPointOut->X - BlackPointIn->X) / tx;
by = - cmsD50_XYZ()-> Y * (BlackPointOut->Y - BlackPointIn->Y) / ty;
bz = - cmsD50_XYZ()-> Z * (BlackPointOut->Z - BlackPointIn->Z) / tz;
_cmsVEC3init(&m ->v[0], ax, 0, 0);
_cmsVEC3init(&m ->v[1], 0, ay, 0);
_cmsVEC3init(&m ->v[2], 0, 0, az);
_cmsVEC3init(off, bx, by, bz);
}
static
cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad)
{
cmsVEC3 d, s;
cmsCIEXYZ Dest;
cmsCIExyY DestChromaticity;
cmsFloat64Number TempK;
cmsMAT3 m1, m2;
m1 = *Chad;
if (!_cmsMAT3inverse(&m1, &m2)) return FALSE;
s.n[VX] = cmsD50_XYZ() -> X;
s.n[VY] = cmsD50_XYZ() -> Y;
s.n[VZ] = cmsD50_XYZ() -> Z;
_cmsMAT3eval(&d, &m2, &s);
Dest.X = d.n[VX];
Dest.Y = d.n[VY];
Dest.Z = d.n[VZ];
cmsXYZ2xyY(&DestChromaticity, &Dest);
if (!cmsTempFromWhitePoint(&TempK, &DestChromaticity))
return -1.0;
return TempK;
}
static
void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp)
{
cmsCIEXYZ White;
cmsCIExyY ChromaticityOfWhite;
cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp);
cmsxyY2XYZ(&White, &ChromaticityOfWhite);
_cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ());
}
static
cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState,
const cmsCIEXYZ* WhitePointIn,
const cmsMAT3* ChromaticAdaptationMatrixIn,
const cmsCIEXYZ* WhitePointOut,
const cmsMAT3* ChromaticAdaptationMatrixOut,
cmsMAT3* m)
{
cmsMAT3 Scale, m1, m2, m3, m4;
if (AdaptationState == 1.0) {
_cmsVEC3init(&m->v[0], WhitePointIn->X / WhitePointOut->X, 0, 0);
_cmsVEC3init(&m->v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0);
_cmsVEC3init(&m->v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z);
}
else {
_cmsVEC3init(&Scale.v[0], WhitePointIn->X / WhitePointOut->X, 0, 0);
_cmsVEC3init(&Scale.v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0);
_cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z);
if (AdaptationState == 0.0) {
m1 = *ChromaticAdaptationMatrixOut;
_cmsMAT3per(&m2, &m1, &Scale);
_cmsMAT3per(m, &m2, ChromaticAdaptationMatrixOut);
m3 = *ChromaticAdaptationMatrixIn;
if (!_cmsMAT3inverse(&m3, &m4)) return FALSE;
_cmsMAT3per(m, &m2, &m4);
} else {
cmsMAT3 MixedCHAD;
cmsFloat64Number TempSrc, TempDest, Temp;
m1 = *ChromaticAdaptationMatrixIn;
if (!_cmsMAT3inverse(&m1, &m2)) return FALSE;
_cmsMAT3per(&m3, &m2, &Scale);
TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn);
TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut);
if (TempSrc < 0.0 || TempDest < 0.0) return FALSE;
if (_cmsMAT3isIdentity(&Scale) && fabs(TempSrc - TempDest) < 0.01) {
_cmsMAT3identity(m);
return TRUE;
}
Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc;
Temp2CHAD(&MixedCHAD, Temp);
_cmsMAT3per(m, &m3, &MixedCHAD);
}
}
return TRUE;
}
static
cmsBool IsEmptyLayer(cmsMAT3* m, cmsVEC3* off)
{
cmsFloat64Number diff = 0;
cmsMAT3 Ident;
int i;
if (m == NULL && off == NULL) return TRUE;
if (m == NULL && off != NULL) return FALSE;
_cmsMAT3identity(&Ident);
for (i=0; i < 3*3; i++)
diff += fabs(((cmsFloat64Number*)m)[i] - ((cmsFloat64Number*)&Ident)[i]);
for (i=0; i < 3; i++)
diff += fabs(((cmsFloat64Number*)off)[i]);
return (diff < 0.002);
}
static
cmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[],
cmsUInt32Number Intent,
cmsBool BPC,
cmsFloat64Number AdaptationState,
cmsMAT3* m, cmsVEC3* off)
{
int k;
_cmsMAT3identity(m);
_cmsVEC3init(off, 0, 0, 0);
if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) {
cmsCIEXYZ WhitePointIn, WhitePointOut;
cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut;
_cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]);
_cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]);
_cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]);
_cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]);
if (!ComputeAbsoluteIntent(AdaptationState,
&WhitePointIn, &ChromaticAdaptationMatrixIn,
&WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE;
}
else {
if (BPC) {
cmsCIEXYZ BlackPointIn, BlackPointOut;
cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0);
cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0);
if (BlackPointIn.X != BlackPointOut.X ||
BlackPointIn.Y != BlackPointOut.Y ||
BlackPointIn.Z != BlackPointOut.Z)
ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off);
}
}
for (k=0; k < 3; k++) {
off ->n[k] /= MAX_ENCODEABLE_XYZ;
}
return TRUE;
}
static
cmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColorSpaceSignature OutPCS, cmsMAT3* m, cmsVEC3* off)
{
cmsFloat64Number* m_as_dbl = (cmsFloat64Number*) m;
cmsFloat64Number* off_as_dbl = (cmsFloat64Number*) off;
switch (InPCS) {
case cmsSigXYZData:
switch (OutPCS) {
case cmsSigXYZData:
if (!IsEmptyLayer(m, off) &&
!cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)))
return FALSE;
break;
case cmsSigLabData:
if (!IsEmptyLayer(m, off) &&
!cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)))
return FALSE;
if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID)))
return FALSE;
break;
default:
return FALSE;
}
break;
case cmsSigLabData:
switch (OutPCS) {
case cmsSigXYZData:
if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)))
return FALSE;
if (!IsEmptyLayer(m, off) &&
!cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)))
return FALSE;
break;
case cmsSigLabData:
if (!IsEmptyLayer(m, off)) {
if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)) ||
!cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)) ||
!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID)))
return FALSE;
}
break;
default:
return FALSE;
}
break;
default:
if (InPCS != OutPCS) return FALSE;
break;
}
return TRUE;
}
static
cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature b)
{
if (a == b) return TRUE;
if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE;
if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE;
if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE;
if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE;
return FALSE;
}
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;
if (nProfiles == 0) return NULL;
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 );
if ((i == 0) && !lIsDeviceLink) {
lIsInput = TRUE;
}
else {
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 (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) {
Lut = _cmsReadDevicelinkLUT(hProfile, Intent);
if (Lut == NULL) goto Error;
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) {
Lut = _cmsReadInputLUT(hProfile, Intent);
if (Lut == NULL) goto Error;
}
else {
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;
}
}
if (!cmsPipelineCat(Result, Lut))
goto Error;
cmsPipelineFree(Lut);
Lut = NULL;
CurrentColorSpace = ColorSpaceOut;
}
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);
}
cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number TheIntents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags)
{
return DefaultICCintents(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags);
}
static
int TranslateNonICCIntents(int Intent)
{
switch (Intent) {
case INTENT_PRESERVE_K_ONLY_PERCEPTUAL:
case INTENT_PRESERVE_K_PLANE_PERCEPTUAL:
return INTENT_PERCEPTUAL;
case INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC:
case INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC:
return INTENT_RELATIVE_COLORIMETRIC;
case INTENT_PRESERVE_K_ONLY_SATURATION:
case INTENT_PRESERVE_K_PLANE_SATURATION:
return INTENT_SATURATION;
default: return Intent;
}
}
typedef struct {
cmsPipeline* cmyk2cmyk;
cmsToneCurve* KTone;
} GrayOnlyParams;
static
int BlackPreservingGrayOnlySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
{
GrayOnlyParams* bp = (GrayOnlyParams*) Cargo;
if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
Out[0] = Out[1] = Out[2] = 0;
Out[3] = cmsEvalToneCurve16(bp->KTone, In[3]);
return TRUE;
}
bp ->cmyk2cmyk ->Eval16Fn(In, Out, bp ->cmyk2cmyk->Data);
return TRUE;
}
static
cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number TheIntents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags)
{
GrayOnlyParams bp;
cmsPipeline* Result;
cmsUInt32Number ICCIntents[256];
cmsStage* CLUT;
cmsUInt32Number i, nGridPoints;
if (nProfiles < 1 || nProfiles > 255) return NULL;
for (i=0; i < nProfiles; i++)
ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]);
if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||
cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigCmykData)
return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags);
memset(&bp, 0, sizeof(bp));
Result = cmsPipelineAlloc(ContextID, 4, 4);
if (Result == NULL) return NULL;
bp.cmyk2cmyk = DefaultICCintents(ContextID,
nProfiles,
ICCIntents,
hProfiles,
BPC,
AdaptationStates,
dwFlags);
if (bp.cmyk2cmyk == NULL) goto Error;
bp.KTone = _cmsBuildKToneCurve(ContextID,
4096,
nProfiles,
ICCIntents,
hProfiles,
BPC,
AdaptationStates,
dwFlags);
if (bp.KTone == NULL) goto Error;
nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags);
CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL);
if (CLUT == NULL) goto Error;
if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT))
goto Error;
if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0))
goto Error;
cmsPipelineFree(bp.cmyk2cmyk);
cmsFreeToneCurve(bp.KTone);
return Result;
Error:
if (bp.cmyk2cmyk != NULL) cmsPipelineFree(bp.cmyk2cmyk);
if (bp.KTone != NULL) cmsFreeToneCurve(bp.KTone);
if (Result != NULL) cmsPipelineFree(Result);
return NULL;
}
typedef struct {
cmsPipeline* cmyk2cmyk;
cmsHTRANSFORM hProofOutput;
cmsHTRANSFORM cmyk2Lab;
cmsToneCurve* KTone;
cmsPipeline* LabK2cmyk;
cmsFloat64Number MaxError;
cmsHTRANSFORM hRoundTrip;
cmsFloat64Number MaxTAC;
} PreserveKPlaneParams;
static
int BlackPreservingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
{
int i;
cmsFloat32Number Inf[4], Outf[4];
cmsFloat32Number LabK[4];
cmsFloat64Number SumCMY, SumCMYK, Error, Ratio;
cmsCIELab ColorimetricLab, BlackPreservingLab;
PreserveKPlaneParams* bp = (PreserveKPlaneParams*) Cargo;
for (i=0; i < 4; i++)
Inf[i] = (cmsFloat32Number) (In[i] / 65535.0);
LabK[3] = cmsEvalToneCurveFloat(bp ->KTone, Inf[3]);
if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
Out[0] = Out[1] = Out[2] = 0;
Out[3] = _cmsQuickSaturateWord(LabK[3] * 65535.0);
return TRUE;
}
cmsPipelineEvalFloat( Inf, Outf, bp ->cmyk2cmyk);
for (i=0; i < 4; i++)
Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0);
if ( fabs(Outf[3] - LabK[3]) < (3.0 / 65535.0) ) {
return TRUE;
}
cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);
cmsDoTransform(bp ->cmyk2Lab, Outf, LabK, 1);
if (!cmsPipelineEvalReverseFloat(LabK, Outf, Outf, bp ->LabK2cmyk)) {
return TRUE;
}
Outf[3] = LabK[3];
SumCMY = Outf[0] + Outf[1] + Outf[2];
SumCMYK = SumCMY + Outf[3];
if (SumCMYK > bp ->MaxTAC) {
Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY);
if (Ratio < 0)
Ratio = 0;
}
else
Ratio = 1.0;
Out[0] = _cmsQuickSaturateWord(Outf[0] * Ratio * 65535.0);
Out[1] = _cmsQuickSaturateWord(Outf[1] * Ratio * 65535.0);
Out[2] = _cmsQuickSaturateWord(Outf[2] * Ratio * 65535.0);
Out[3] = _cmsQuickSaturateWord(Outf[3] * 65535.0);
cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
if (Error > bp -> MaxError)
bp->MaxError = Error;
return TRUE;
}
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;
if (nProfiles < 1 || nProfiles > 255) return NULL;
for (i=0; i < nProfiles; i++)
ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]);
if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||
!(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData ||
cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass))
return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags);
Result = cmsPipelineAlloc(ContextID, 4, 4);
if (Result == NULL) return NULL;
memset(&bp, 0, sizeof(bp));
bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC);
if (bp.LabK2cmyk == NULL) goto Cleanup;
bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0;
if (bp.MaxTAC <= 0) goto Cleanup;
bp.cmyk2cmyk = DefaultICCintents(ContextID,
nProfiles,
ICCIntents,
hProfiles,
BPC,
AdaptationStates,
dwFlags);
if (bp.cmyk2cmyk == NULL) goto Cleanup;
bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles,
ICCIntents,
hProfiles,
BPC,
AdaptationStates,
dwFlags);
if (bp.KTone == NULL) goto Cleanup;
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;
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);
bp.MaxError = 0;
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;
}
cmsPipeline* _cmsLinkProfiles(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number TheIntents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags)
{
cmsUInt32Number i;
cmsIntentsList* Intent;
if (nProfiles <= 0 || nProfiles > 255) {
cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't link '%d' profiles", nProfiles);
return NULL;
}
for (i=0; i < nProfiles; i++) {
if (TheIntents[i] == INTENT_ABSOLUTE_COLORIMETRIC)
BPC[i] = FALSE;
if (TheIntents[i] == INTENT_PERCEPTUAL || TheIntents[i] == INTENT_SATURATION) {
if (cmsGetEncodedICCversion(hProfiles[i]) >= 0x4000000)
BPC[i] = TRUE;
}
}
Intent = SearchIntent(ContextID, TheIntents[0]);
if (Intent == NULL) {
cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]);
return NULL;
}
return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags);
}
cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions)
{
_cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin);
cmsIntentsList* pt;
cmsUInt32Number nIntents;
for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next)
{
if (nIntents < nMax) {
if (Codes != NULL)
Codes[nIntents] = pt ->Intent;
if (Descriptions != NULL)
Descriptions[nIntents] = pt ->Description;
}
nIntents++;
}
for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next)
{
if (nIntents < nMax) {
if (Codes != NULL)
Codes[nIntents] = pt ->Intent;
if (Descriptions != NULL)
Descriptions[nIntents] = pt ->Description;
}
nIntents++;
}
return nIntents;
}
cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions)
{
return cmsGetSupportedIntentsTHR(NULL, nMax, Codes, Descriptions);
}
cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext id, cmsPluginBase* Data)
{
_cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(id, IntentPlugin);
cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data;
cmsIntentsList* fl;
if (Data == NULL) {
ctx->Intents = NULL;
return TRUE;
}
fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList));
if (fl == NULL) return FALSE;
fl ->Intent = Plugin ->Intent;
strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1);
fl ->Description[sizeof(fl ->Description)-1] = 0;
fl ->Link = Plugin ->Link;
fl ->Next = ctx ->Intents;
ctx ->Intents = fl;
return TRUE;
}