Beispiel #1
0
CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
                                                  cmsUInt32Number InputFormat,
                                                  cmsHPROFILE Output,
                                                  cmsUInt32Number OutputFormat,
                                                  cmsUInt32Number Intent,
                                                  cmsUInt32Number dwFlags)
{
    return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
}
Beispiel #2
0
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;
}
Beispiel #3
0
// 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;


}
Beispiel #4
0
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) {

        cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL));
        cmsStage* mpe = cmsStageAllocToneCurves(ContextID, 3, InvShapes);
        if (mpe)
          cmsPipelineInsertStage(Lut, cmsAT_END, mpe);
        else  {
          cmsPipelineFree(Lut);
          Lut = NULL;
        }
    }

    cmsFreeToneCurveTriple(InvShapes);
    return Lut;
}
Beispiel #5
0
// 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);

        // 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);
}
Beispiel #6
0
// 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;
}
Beispiel #7
0
// 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;
}
Beispiel #8
0
// Detect Total area coverage of the profile
cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile)
{
    cmsTACestimator bp;
    cmsUInt32Number dwFormatter;
    cmsUInt32Number GridPoints[MAX_INPUT_DIMENSIONS];
    cmsHPROFILE hLab;
    cmsContext ContextID = cmsGetProfileContextID(hProfile);

    // TAC only works on output profiles
    if (cmsGetDeviceClass(hProfile) != cmsSigOutputClass) {
        return 0;
    }

    // Create a fake formatter for result
    dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE);

    bp.nOutputChans = T_CHANNELS(dwFormatter);
    bp.MaxTAC = 0;    // Initial TAC is 0

    //  for safety
    if (bp.nOutputChans >= cmsMAXCHANNELS) return 0;

    hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
    if (hLab == NULL) return 0;
    // Setup a roundtrip on perceptual intent in output profile for TAC estimation
    bp.hRoundTrip = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_16,
                                          hProfile, dwFormatter, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);

    cmsCloseProfile(hLab);
    if (bp.hRoundTrip == NULL) return 0;

    // For L* we only need black and white. For C* we need many points
    GridPoints[0] = 6;
    GridPoints[1] = 74;
    GridPoints[2] = 74;


    if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) {
        bp.MaxTAC = 0;
    }

    cmsDeleteTransform(bp.hRoundTrip);

    // Results in %
    return bp.MaxTAC;
}
Beispiel #9
0
cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
                                                   cmsUInt32Number InputFormat,
                                                   cmsHPROFILE OutputProfile,
                                                   cmsUInt32Number OutputFormat,
                                                   cmsHPROFILE ProofingProfile,
                                                   cmsUInt32Number nIntent,
                                                   cmsUInt32Number ProofingIntent,
                                                   cmsUInt32Number dwFlags)
{
    return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
                                                   InputProfile,
                                                   InputFormat,
                                                   OutputProfile,
                                                   OutputFormat,
                                                   ProofingProfile,
                                                   nIntent,
                                                   ProofingIntent,
                                                   dwFlags);
}
Beispiel #10
0
// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
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 accommodated to 0..1.0, we should undo this change
    if ( PCS == cmsSigLabData)
    {
        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
            goto Error;
    }
    else
        if (PCS == cmsSigXYZData)
        {
            if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
                goto Error;
        }
    
    // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline
    if ( dataSpace == cmsSigLabData)
    {
        if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
            goto Error;
    }
    else if (dataSpace == cmsSigXYZData)
    {
        if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
            goto Error;
    }
    
    return Lut;

Error:
    cmsPipelineFree(Lut);
    return NULL;
}
Beispiel #11
0
// PCS -> PCS round trip transform, always uses relative intent on the device -> pcs 
static
cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent)
{
    cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
    cmsHTRANSFORM xform;
    cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE };
    cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 };
    cmsHPROFILE hProfiles[4];
    cmsUInt32Number Intents[4];
    cmsContext ContextID = cmsGetProfileContextID(hProfile);

    hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab;
    Intents[0]   = INTENT_RELATIVE_COLORIMETRIC; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = INTENT_RELATIVE_COLORIMETRIC;

    xform =  cmsCreateExtendedTransform(ContextID, 4, hProfiles, BPC, Intents, 
        States, NULL, 0, TYPE_Lab_DBL, TYPE_Lab_DBL, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);

    cmsCloseProfile(hLab);
    return xform;
}
Beispiel #12
0
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
                                                  cmsUInt32Number nProfiles,
                                                  cmsUInt32Number InputFormat,
                                                  cmsUInt32Number OutputFormat,
                                                  cmsUInt32Number Intent,
                                                  cmsUInt32Number dwFlags)
{

    if (nProfiles <= 0 || nProfiles > 255) {
         cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
         return NULL;
    }

    return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
                                                  hProfiles,
                                                  nProfiles,
                                                  InputFormat,
                                                  OutputFormat,
                                                  Intent,
                                                  dwFlags);
}
Beispiel #13
0
// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
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)
    {
        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
            goto Error;
    }
    else if (spc == cmsSigXYZData)
    {
        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
            goto Error;
    }
    
    if ( PCS == cmsSigLabData)
    {
        if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
            goto Error;
    }
    else if( PCS == cmsSigXYZData)
    {
        if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
            goto Error;
    }
    
    return Lut;

Error:
    cmsPipelineFree(Lut);
    return NULL;
}
Beispiel #14
0
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) {

        if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1,  3, PickLstarMatrix, NULL)))
            goto Error;
    }
    else  {
        if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1,  3, PickYMatrix, NULL)))
            goto Error;
    }

    if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC)))
        goto Error;

    cmsFreeToneCurve(RevGrayTRC);
    return Lut;

Error:
    cmsFreeToneCurve(RevGrayTRC);
    cmsPipelineFree(Lut);
    return NULL;
}
Beispiel #15
0
// 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)
    {
        if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
            goto Error;
    }
    else
        if (spc == cmsSigXYZData)
        {
            if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
                goto Error;
        }

        if (PCS == cmsSigLabData)
        {
            if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
                goto Error;
        }
        else
            if (PCS == cmsSigXYZData)
            {
                if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
                    goto Error;
            }

    return Lut;
Error:
    cmsPipelineFree(Lut);
    return NULL;
}
Beispiel #16
0
// 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) {

        cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes));
        cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL));
    }

    return Lut;
}
Beispiel #17
0
// 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;
}
Beispiel #18
0
// Create an output MPE LUT from agiven profile. Version mismatches are handled here
cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
{
    cmsTagTypeSignature OriginalType;
    cmsTagSignature tag16;
    cmsTagSignature tagFloat;
    cmsContext ContextID  = cmsGetProfileContextID(hProfile);


    if (Intent >= INTENT_PERCEPTUAL && Intent <= INTENT_ABSOLUTE_COLORIMETRIC) {

        tag16 = PCS2Device16[Intent];
        tagFloat = PCS2DeviceFloat[Intent];

        if (cmsIsTag(hProfile, tagFloat)) {  // Float tag takes precedence

            // Floating point LUT are always V4
            return _cmsReadFloatOutputTag(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
            if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
                goto Error;

            // If the output is Lab, add also a conversion at the end
            if (cmsGetColorSpace(hProfile) == cmsSigLabData)
                if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
                    goto Error;

            return Lut;
Error:
            cmsPipelineFree(Lut);
            return NULL;
        }
    }

    // Lut 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 BuildGrayOutputPipeline(hProfile);
    }

    // Not gray, create a normal matrix-shaper, which only operates in XYZ space  
    return BuildRGBOutputMatrixShaper(hProfile);
}
Beispiel #19
0
// 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);
}
Beispiel #20
0
// 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);

    // On named color, take the appropiate 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;
        }

        cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE));
        cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID));
        return Lut;
    }

    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);
}
Beispiel #21
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);
}