alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (cmslut.c)

This example Java source code file (cmslut.c) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

cmsexport, contextid, data, elements, false, inputchannels, newelem, newlut, newmpe, next, null, out, outputchannels

The cmslut.c Java example source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
//  Little Color Management System
//  Copyright (c) 1998-2012 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//---------------------------------------------------------------------------------
//

#include "lcms2_internal.h"


// Allocates an empty multi profile element
cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID,
                                cmsStageSignature Type,
                                cmsUInt32Number InputChannels,
                                cmsUInt32Number OutputChannels,
                                _cmsStageEvalFn     EvalPtr,
                                _cmsStageDupElemFn  DupElemPtr,
                                _cmsStageFreeElemFn FreePtr,
                                void*             Data)
{
    cmsStage* ph = (cmsStage*) _cmsMallocZero(ContextID, sizeof(cmsStage));

    if (ph == NULL) return NULL;


    ph ->ContextID = ContextID;

    ph ->Type       = Type;
    ph ->Implements = Type;   // By default, no clue on what is implementing

    ph ->InputChannels  = InputChannels;
    ph ->OutputChannels = OutputChannels;
    ph ->EvalPtr        = EvalPtr;
    ph ->DupElemPtr     = DupElemPtr;
    ph ->FreePtr        = FreePtr;
    ph ->Data           = Data;

    return ph;
}


static
void EvaluateIdentity(const cmsFloat32Number In[],
                            cmsFloat32Number Out[],
                      const cmsStage *mpe)
{
    memmove(Out, In, mpe ->InputChannels * sizeof(cmsFloat32Number));
}


cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels)
{
    return _cmsStageAllocPlaceholder(ContextID,
                                   cmsSigIdentityElemType,
                                   nChannels, nChannels,
                                   EvaluateIdentity,
                                   NULL,
                                   NULL,
                                   NULL);
 }

// Conversion functions. From floating point to 16 bits
static
void FromFloatTo16(const cmsFloat32Number In[], cmsUInt16Number Out[], cmsUInt32Number n)
{
    cmsUInt32Number i;

    for (i=0; i < n; i++) {
        Out[i] = _cmsQuickSaturateWord(In[i] * 65535.0);
    }
}

// From 16 bits to floating point
static
void From16ToFloat(const cmsUInt16Number In[], cmsFloat32Number Out[], cmsUInt32Number n)
{
    cmsUInt32Number i;

    for (i=0; i < n; i++) {
        Out[i] = (cmsFloat32Number) In[i] / 65535.0F;
    }
}


// This function is quite useful to analyze the structure of a LUT and retrieve the MPE elements
// that conform the LUT. It should be called with the LUT, the number of expected elements and
// then a list of expected types followed with a list of cmsFloat64Number pointers to MPE elements. If
// the function founds a match with current pipeline, it fills the pointers and returns TRUE
// if not, returns FALSE without touching anything. Setting pointers to NULL does bypass
// the storage process.
cmsBool  CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...)
{
    va_list args;
    cmsUInt32Number i;
    cmsStage* mpe;
    cmsStageSignature Type;
    void** ElemPtr;

    // Make sure same number of elements
    if (cmsPipelineStageCount(Lut) != n) return FALSE;

    va_start(args, n);

    // Iterate across asked types
    mpe = Lut ->Elements;
    for (i=0; i < n; i++) {

        // Get asked type
        Type  = (cmsStageSignature)va_arg(args, cmsStageSignature);
        if (mpe ->Type != Type) {

            va_end(args);       // Mismatch. We are done.
            return FALSE;
        }
        mpe = mpe ->Next;
    }

    // Found a combination, fill pointers if not NULL
    mpe = Lut ->Elements;
    for (i=0; i < n; i++) {

        ElemPtr = va_arg(args, void**);
        if (ElemPtr != NULL)
            *ElemPtr = mpe;

        mpe = mpe ->Next;
    }

    va_end(args);
    return TRUE;
}

// Below there are implementations for several types of elements. Each type may be implemented by a
// evaluation function, a duplication function, a function to free resources and a constructor.

// *************************************************************************************************
// Type cmsSigCurveSetElemType (curves)
// *************************************************************************************************

cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe)
{
    _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data;

    return Data ->TheCurves;
}

static
void EvaluateCurves(const cmsFloat32Number In[],
                    cmsFloat32Number Out[],
                    const cmsStage *mpe)
{
    _cmsStageToneCurvesData* Data;
    cmsUInt32Number i;

    _cmsAssert(mpe != NULL);

    Data = (_cmsStageToneCurvesData*) mpe ->Data;
    if (Data == NULL) return;

    if (Data ->TheCurves == NULL) return;

    for (i=0; i < Data ->nCurves; i++) {
        Out[i] = cmsEvalToneCurveFloat(Data ->TheCurves[i], In[i]);
    }
}

static
void CurveSetElemTypeFree(cmsStage* mpe)
{
    _cmsStageToneCurvesData* Data;
    cmsUInt32Number i;

    _cmsAssert(mpe != NULL);

    Data = (_cmsStageToneCurvesData*) mpe ->Data;
    if (Data == NULL) return;

    if (Data ->TheCurves != NULL) {
        for (i=0; i < Data ->nCurves; i++) {
            if (Data ->TheCurves[i] != NULL)
                cmsFreeToneCurve(Data ->TheCurves[i]);
        }
    }
    _cmsFree(mpe ->ContextID, Data ->TheCurves);
    _cmsFree(mpe ->ContextID, Data);
}


static
void* CurveSetDup(cmsStage* mpe)
{
    _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data;
    _cmsStageToneCurvesData* NewElem;
    cmsUInt32Number i;

    NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageToneCurvesData));
    if (NewElem == NULL) return NULL;

    NewElem ->nCurves   = Data ->nCurves;
    NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(mpe ->ContextID, NewElem ->nCurves, sizeof(cmsToneCurve*));

    if (NewElem ->TheCurves == NULL) goto Error;

    for (i=0; i < NewElem ->nCurves; i++) {

        // Duplicate each curve. It may fail.
        NewElem ->TheCurves[i] = cmsDupToneCurve(Data ->TheCurves[i]);
        if (NewElem ->TheCurves[i] == NULL) goto Error;


    }
    return (void*) NewElem;

Error:

    if (NewElem ->TheCurves != NULL) {
        for (i=0; i < NewElem ->nCurves; i++) {
            if (NewElem ->TheCurves[i])
                cmsFreeToneCurve(Data ->TheCurves[i]);
        }
    }
    _cmsFree(mpe ->ContextID, Data ->TheCurves);
    _cmsFree(mpe ->ContextID, NewElem);
    return NULL;
}


// Curves == NULL forces identity curves
cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[])
{
    cmsUInt32Number i;
    _cmsStageToneCurvesData* NewElem;
    cmsStage* NewMPE;


    NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCurveSetElemType, nChannels, nChannels,
                                     EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL );
    if (NewMPE == NULL) return NULL;

    NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData));
    if (NewElem == NULL) {
        cmsStageFree(NewMPE);
        return NULL;
    }

    NewMPE ->Data  = (void*) NewElem;

    NewElem ->nCurves   = nChannels;
    NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, nChannels, sizeof(cmsToneCurve*));
    if (NewElem ->TheCurves == NULL) {
        cmsStageFree(NewMPE);
        return NULL;
    }

    for (i=0; i < nChannels; i++) {

        if (Curves == NULL) {
            NewElem ->TheCurves[i] = cmsBuildGamma(ContextID, 1.0);
        }
        else {
            NewElem ->TheCurves[i] = cmsDupToneCurve(Curves[i]);
        }

        if (NewElem ->TheCurves[i] == NULL) {
            cmsStageFree(NewMPE);
            return NULL;
        }

    }

   return NewMPE;
}


// Create a bunch of identity curves
cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels)
{
    cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL);

    if (mpe == NULL) return NULL;
    mpe ->Implements = cmsSigIdentityElemType;
    return mpe;
}


// *************************************************************************************************
// Type cmsSigMatrixElemType (Matrices)
// *************************************************************************************************


// Special care should be taken here because precision loss. A temporary cmsFloat64Number buffer is being used
static
void EvaluateMatrix(const cmsFloat32Number In[],
                    cmsFloat32Number Out[],
                    const cmsStage *mpe)
{
    cmsUInt32Number i, j;
    _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
    cmsFloat64Number Tmp;

    // Input is already in 0..1.0 notation
    for (i=0; i < mpe ->OutputChannels; i++) {

        Tmp = 0;
        for (j=0; j < mpe->InputChannels; j++) {
            Tmp += In[j] * Data->Double[i*mpe->InputChannels + j];
        }

        if (Data ->Offset != NULL)
            Tmp += Data->Offset[i];

        Out[i] = (cmsFloat32Number) Tmp;
    }


    // Output in 0..1.0 domain
}


// Duplicate a yet-existing matrix element
static
void* MatrixElemDup(cmsStage* mpe)
{
    _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
    _cmsStageMatrixData* NewElem;
    cmsUInt32Number sz;

    NewElem = (_cmsStageMatrixData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageMatrixData));
    if (NewElem == NULL) return NULL;

    sz = mpe ->InputChannels * mpe ->OutputChannels;

    NewElem ->Double = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, Data ->Double, sz * sizeof(cmsFloat64Number)) ;

    if (Data ->Offset)
        NewElem ->Offset = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID,
                                                Data ->Offset, mpe -> OutputChannels * sizeof(cmsFloat64Number)) ;

    return (void*) NewElem;
}


static
void MatrixElemTypeFree(cmsStage* mpe)
{
    _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
    if (Data ->Double)
        _cmsFree(mpe ->ContextID, Data ->Double);

    if (Data ->Offset)
        _cmsFree(mpe ->ContextID, Data ->Offset);

    _cmsFree(mpe ->ContextID, mpe ->Data);
}



cmsStage*  CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols,
                                     const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset)
{
    cmsUInt32Number i, n;
    _cmsStageMatrixData* NewElem;
    cmsStage* NewMPE;

    n = Rows * Cols;

    // Check for overflow
    if (n == 0) return NULL;
    if (n >= UINT_MAX / Cols) return NULL;
    if (n >= UINT_MAX / Rows) return NULL;
    if (n < Rows || n < Cols) return NULL;

    NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigMatrixElemType, Cols, Rows,
                                     EvaluateMatrix, MatrixElemDup, MatrixElemTypeFree, NULL );
    if (NewMPE == NULL) return NULL;


    NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData));
    if (NewElem == NULL) return NULL;


    NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number));

    if (NewElem->Double == NULL) {
        MatrixElemTypeFree(NewMPE);
        return NULL;
    }

    for (i=0; i < n; i++) {
        NewElem ->Double[i] = Matrix[i];
    }


    if (Offset != NULL) {

        NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Cols, sizeof(cmsFloat64Number));
        if (NewElem->Offset == NULL) {
           MatrixElemTypeFree(NewMPE);
           return NULL;
        }

        for (i=0; i < Cols; i++) {
                NewElem ->Offset[i] = Offset[i];
        }

    }

    NewMPE ->Data  = (void*) NewElem;
    return NewMPE;
}


// *************************************************************************************************
// Type cmsSigCLutElemType
// *************************************************************************************************


// Evaluate in true floating point
static
void EvaluateCLUTfloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
{
    _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;

    Data -> Params ->Interpolation.LerpFloat(In, Out, Data->Params);
}


// Convert to 16 bits, evaluate, and back to floating point
static
void EvaluateCLUTfloatIn16(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
{
    _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;
    cmsUInt16Number In16[MAX_STAGE_CHANNELS], Out16[MAX_STAGE_CHANNELS];

    _cmsAssert(mpe ->InputChannels  <= MAX_STAGE_CHANNELS);
    _cmsAssert(mpe ->OutputChannels <= MAX_STAGE_CHANNELS);

    FromFloatTo16(In, In16, mpe ->InputChannels);
    Data -> Params ->Interpolation.Lerp16(In16, Out16, Data->Params);
    From16ToFloat(Out16, Out,  mpe ->OutputChannels);
}


// Given an hypercube of b dimensions, with Dims[] number of nodes by dimension, calculate the total amount of nodes
static
cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b)
{
    cmsUInt32Number rv, dim;

    _cmsAssert(Dims != NULL);

    for (rv = 1; b > 0; b--) {

        dim = Dims[b-1];
        if (dim == 0) return 0;  // Error

        rv *= dim;

        // Check for overflow
        if (rv > UINT_MAX / dim) return 0;
    }

    return rv;
}

static
void* CLUTElemDup(cmsStage* mpe)
{
    _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;
    _cmsStageCLutData* NewElem;


    NewElem = (_cmsStageCLutData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageCLutData));
    if (NewElem == NULL) return NULL;

    NewElem ->nEntries       = Data ->nEntries;
    NewElem ->HasFloatValues = Data ->HasFloatValues;

    if (Data ->Tab.T) {

        if (Data ->HasFloatValues)
            NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number));
        else
            NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number));
    }

    NewElem ->Params   = _cmsComputeInterpParamsEx(mpe ->ContextID,
                                                   Data ->Params ->nSamples,
                                                   Data ->Params ->nInputs,
                                                   Data ->Params ->nOutputs,
                                                   NewElem ->Tab.T,
                                                   Data ->Params ->dwFlags);

    return (void*) NewElem;
}


static
void CLutElemTypeFree(cmsStage* mpe)
{

    _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;

    // Already empty
    if (Data == NULL) return;

    // This works for both types
    if (Data -> Tab.T)
        _cmsFree(mpe ->ContextID, Data -> Tab.T);

    _cmsFreeInterpParams(Data ->Params);
    _cmsFree(mpe ->ContextID, mpe ->Data);
}


// Allocates a 16-bit multidimensional CLUT. This is evaluated at 16-bit precision. Table may have different
// granularity on each dimension.
cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID,
                                         const cmsUInt32Number clutPoints[],
                                         cmsUInt32Number inputChan,
                                         cmsUInt32Number outputChan,
                                         const cmsUInt16Number* Table)
{
    cmsUInt32Number i, n;
    _cmsStageCLutData* NewElem;
    cmsStage* NewMPE;

    _cmsAssert(clutPoints != NULL);

    if (inputChan > MAX_INPUT_DIMENSIONS) {
        cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS);
        return NULL;
    }

    NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,
                                     EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL );

    if (NewMPE == NULL) return NULL;

    NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));
    if (NewElem == NULL) {
        cmsStageFree(NewMPE);
        return NULL;
    }

    NewMPE ->Data  = (void*) NewElem;

    NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan);
    NewElem -> HasFloatValues = FALSE;

    if (n == 0) {
        cmsStageFree(NewMPE);
        return NULL;
    }


    NewElem ->Tab.T  = (cmsUInt16Number*) _cmsCalloc(ContextID, n, sizeof(cmsUInt16Number));
    if (NewElem ->Tab.T == NULL) {
        cmsStageFree(NewMPE);
        return NULL;
    }

    if (Table != NULL) {
        for (i=0; i < n; i++) {
            NewElem ->Tab.T[i] = Table[i];
        }
    }

    NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.T, CMS_LERP_FLAGS_16BITS);
    if (NewElem ->Params == NULL) {
        cmsStageFree(NewMPE);
        return NULL;
    }

    return NewMPE;
}

cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID,
                                    cmsUInt32Number nGridPoints,
                                    cmsUInt32Number inputChan,
                                    cmsUInt32Number outputChan,
                                    const cmsUInt16Number* Table)
{
    cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
    int i;

   // Our resulting LUT would be same gridpoints on all dimensions
    for (i=0; i < MAX_INPUT_DIMENSIONS; i++)
        Dimensions[i] = nGridPoints;


    return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table);
}


cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID,
                                       cmsUInt32Number nGridPoints,
                                       cmsUInt32Number inputChan,
                                       cmsUInt32Number outputChan,
                                       const cmsFloat32Number* Table)
{
   cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
   int i;

    // Our resulting LUT would be same gridpoints on all dimensions
    for (i=0; i < MAX_INPUT_DIMENSIONS; i++)
        Dimensions[i] = nGridPoints;

    return cmsStageAllocCLutFloatGranular(ContextID, Dimensions, inputChan, outputChan, Table);
}



cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table)
{
    cmsUInt32Number i, n;
    _cmsStageCLutData* NewElem;
    cmsStage* NewMPE;

    _cmsAssert(clutPoints != NULL);

    if (inputChan > MAX_INPUT_DIMENSIONS) {
        cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS);
        return NULL;
    }

    NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,
                                             EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL);
    if (NewMPE == NULL) return NULL;


    NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));
    if (NewElem == NULL) {
        cmsStageFree(NewMPE);
        return NULL;
    }

    NewMPE ->Data  = (void*) NewElem;

    // There is a potential integer overflow on conputing n and nEntries.
    NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan);
    NewElem -> HasFloatValues = TRUE;

    if (n == 0) {
        cmsStageFree(NewMPE);
        return NULL;
    }

    NewElem ->Tab.TFloat  = (cmsFloat32Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat32Number));
    if (NewElem ->Tab.TFloat == NULL) {
        cmsStageFree(NewMPE);
        return NULL;
    }

    if (Table != NULL) {
        for (i=0; i < n; i++) {
            NewElem ->Tab.TFloat[i] = Table[i];
        }
    }


    NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints,  inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT);
    if (NewElem ->Params == NULL) {
        cmsStageFree(NewMPE);
        return NULL;
    }



    return NewMPE;
}


static
int IdentitySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo)
{
    int nChan = *(int*) Cargo;
    int i;

    for (i=0; i < nChan; i++)
        Out[i] = In[i];

    return 1;
}

// Creates an MPE that just copies input to output
cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan)
{
    cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
    cmsStage* mpe ;
    int i;

    for (i=0; i < MAX_INPUT_DIMENSIONS; i++)
        Dimensions[i] = 2;

    mpe = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, nChan, nChan, NULL);
    if (mpe == NULL) return NULL;

    if (!cmsStageSampleCLut16bit(mpe, IdentitySampler, &nChan, 0)) {
        cmsStageFree(mpe);
        return NULL;
    }

    mpe ->Implements = cmsSigIdentityElemType;
    return mpe;
}



// Quantize a value 0 <= i < MaxSamples to 0..0xffff
cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, int MaxSamples)
{
    cmsFloat64Number x;

    x = ((cmsFloat64Number) i * 65535.) / (cmsFloat64Number) (MaxSamples - 1);
    return _cmsQuickSaturateWord(x);
}


// This routine does a sweep on whole input space, and calls its callback
// function on knots. returns TRUE if all ok, FALSE otherwise.
cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags)
{
    int i, t, nTotalPoints, index, rest;
    int nInputs, nOutputs;
    cmsUInt32Number* nSamples;
    cmsUInt16Number In[cmsMAXCHANNELS], Out[MAX_STAGE_CHANNELS];
    _cmsStageCLutData* clut;

    if (mpe == NULL) return FALSE;

    clut = (_cmsStageCLutData*) mpe->Data;

    if (clut == NULL) return FALSE;

    nSamples = clut->Params ->nSamples;
    nInputs  = clut->Params ->nInputs;
    nOutputs = clut->Params ->nOutputs;

    if (nInputs >= cmsMAXCHANNELS) return FALSE;
    if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;

    nTotalPoints = CubeSize(nSamples, nInputs);
    if (nTotalPoints == 0) return FALSE;

    index = 0;
    for (i = 0; i < nTotalPoints; i++) {

        rest = i;
        for (t = nInputs-1; t >=0; --t) {

            cmsUInt32Number  Colorant = rest % nSamples[t];

            rest /= nSamples[t];

            In[t] = _cmsQuantizeVal(Colorant, nSamples[t]);
        }

        if (clut ->Tab.T != NULL) {
            for (t=0; t < nOutputs; t++)
                Out[t] = clut->Tab.T[index + t];
        }

        if (!Sampler(In, Out, Cargo))
            return FALSE;

        if (!(dwFlags & SAMPLER_INSPECT)) {

            if (clut ->Tab.T != NULL) {
                for (t=0; t < nOutputs; t++)
                    clut->Tab.T[index + t] = Out[t];
            }
        }

        index += nOutputs;
    }

    return TRUE;
}

// Same as anterior, but for floting point
cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags)
{
    int i, t, nTotalPoints, index, rest;
    int nInputs, nOutputs;
    cmsUInt32Number* nSamples;
    cmsFloat32Number In[cmsMAXCHANNELS], Out[MAX_STAGE_CHANNELS];
    _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data;

    nSamples = clut->Params ->nSamples;
    nInputs  = clut->Params ->nInputs;
    nOutputs = clut->Params ->nOutputs;

    if (nInputs >= cmsMAXCHANNELS) return FALSE;
    if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;

    nTotalPoints = CubeSize(nSamples, nInputs);
    if (nTotalPoints == 0) return FALSE;

    index = 0;
    for (i = 0; i < nTotalPoints; i++) {

        rest = i;
        for (t = nInputs-1; t >=0; --t) {

            cmsUInt32Number  Colorant = rest % nSamples[t];

            rest /= nSamples[t];

            In[t] =  (cmsFloat32Number) (_cmsQuantizeVal(Colorant, nSamples[t]) / 65535.0);
        }

        if (clut ->Tab.TFloat != NULL) {
            for (t=0; t < nOutputs; t++)
                Out[t] = clut->Tab.TFloat[index + t];
        }

        if (!Sampler(In, Out, Cargo))
            return FALSE;

        if (!(dwFlags & SAMPLER_INSPECT)) {

            if (clut ->Tab.TFloat != NULL) {
                for (t=0; t < nOutputs; t++)
                    clut->Tab.TFloat[index + t] = Out[t];
            }
        }

        index += nOutputs;
    }

    return TRUE;
}



// This routine does a sweep on whole input space, and calls its callback
// function on knots. returns TRUE if all ok, FALSE otherwise.
cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],
                                         cmsSAMPLER16 Sampler, void * Cargo)
{
    int i, t, nTotalPoints, rest;
    cmsUInt16Number In[cmsMAXCHANNELS];

    if (nInputs >= cmsMAXCHANNELS) return FALSE;

    nTotalPoints = CubeSize(clutPoints, nInputs);
    if (nTotalPoints == 0) return FALSE;

    for (i = 0; i < nTotalPoints; i++) {

        rest = i;
        for (t = nInputs-1; t >=0; --t) {

            cmsUInt32Number  Colorant = rest % clutPoints[t];

            rest /= clutPoints[t];
            In[t] = _cmsQuantizeVal(Colorant, clutPoints[t]);

        }

        if (!Sampler(In, NULL, Cargo))
            return FALSE;
    }

    return TRUE;
}

cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],
                                            cmsSAMPLERFLOAT Sampler, void * Cargo)
{
    int i, t, nTotalPoints, rest;
    cmsFloat32Number In[cmsMAXCHANNELS];

    if (nInputs >= cmsMAXCHANNELS) return FALSE;

    nTotalPoints = CubeSize(clutPoints, nInputs);
    if (nTotalPoints == 0) return FALSE;

    for (i = 0; i < nTotalPoints; i++) {

        rest = i;
        for (t = nInputs-1; t >=0; --t) {

            cmsUInt32Number  Colorant = rest % clutPoints[t];

            rest /= clutPoints[t];
            In[t] =  (cmsFloat32Number) (_cmsQuantizeVal(Colorant, clutPoints[t]) / 65535.0);

        }

        if (!Sampler(In, NULL, Cargo))
            return FALSE;
    }

    return TRUE;
}

// ********************************************************************************
// Type cmsSigLab2XYZElemType
// ********************************************************************************


static
void EvaluateLab2XYZ(const cmsFloat32Number In[],
                     cmsFloat32Number Out[],
                     const cmsStage *mpe)
{
    cmsCIELab Lab;
    cmsCIEXYZ XYZ;
    const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ;

    // V4 rules
    Lab.L = In[0] * 100.0;
    Lab.a = In[1] * 255.0 - 128.0;
    Lab.b = In[2] * 255.0 - 128.0;

    cmsLab2XYZ(NULL, &XYZ, &Lab);

    // From XYZ, range 0..19997 to 0..1.0, note that 1.99997 comes from 0xffff
    // encoded as 1.15 fixed point, so 1 + (32767.0 / 32768.0)

    Out[0] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.X / XYZadj);
    Out[1] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Y / XYZadj);
    Out[2] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Z / XYZadj);
    return;

    cmsUNUSED_PARAMETER(mpe);
}


// No dup or free routines needed, as the structure has no pointers in it.
cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID)
{
    return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL);
}

// ********************************************************************************

// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable
// number of gridpoints that would make exact match. However, a prelinearization
// of 258 entries, would map 0xFF00 exactly on entry 257, and this is good to avoid scum dot.
// Almost all what we need but unfortunately, the rest of entries should be scaled by
// (255*257/256) and this is not exact.

cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID)
{
    cmsStage* mpe;
    cmsToneCurve* LabTable[3];
    int i, j;

    LabTable[0] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL);
    LabTable[1] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL);
    LabTable[2] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL);

    for (j=0; j < 3; j++) {

        if (LabTable[j] == NULL) {
            cmsFreeToneCurveTriple(LabTable);
            return NULL;
        }

        // We need to map * (0xffff / 0xff00), thats same as (257 / 256)
        // So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256);
        for (i=0; i < 257; i++)  {

            LabTable[j]->Table16[i] = (cmsUInt16Number) ((i * 0xffff + 0x80) >> 8);
        }

        LabTable[j] ->Table16[257] = 0xffff;
    }

    mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable);
    cmsFreeToneCurveTriple(LabTable);

    if (mpe == NULL) return mpe;

    mpe ->Implements = cmsSigLabV2toV4;
    return mpe;
}

// ********************************************************************************

// Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles
cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID)
{
    static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0,
                                     0, 65535.0/65280.0, 0,
                                     0, 0, 65535.0/65280.0
                                     };

    cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V2ToV4, NULL);

    if (mpe == NULL) return mpe;
    mpe ->Implements = cmsSigLabV2toV4;
    return mpe;
}


// Reverse direction
cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID)
{
    static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0,
                                     0, 65280.0/65535.0, 0,
                                     0, 0, 65280.0/65535.0
                                     };

     cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V4ToV2, NULL);

    if (mpe == NULL) return mpe;
    mpe ->Implements = cmsSigLabV4toV2;
    return mpe;
}


// To Lab to float. Note that the MPE gives numbers in normal Lab range
// and we need 0..1.0 range for the formatters
// L* : 0...100 => 0...1.0  (L* / 100)
// ab* : -128..+127 to 0..1  ((ab* + 128) / 255)

cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID)
{
    static const cmsFloat64Number a1[] = {
        1.0/100.0, 0, 0,
        0, 1.0/255.0, 0,
        0, 0, 1.0/255.0
    };

    static const cmsFloat64Number o1[] = {
        0,
        128.0/255.0,
        128.0/255.0
    };

    cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1);

    if (mpe == NULL) return mpe;
    mpe ->Implements = cmsSigLab2FloatPCS;
    return mpe;
}

// Fom XYZ to floating point PCS
cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID)
{
#define n (32768.0/65535.0)
    static const cmsFloat64Number a1[] = {
        n, 0, 0,
        0, n, 0,
        0, 0, n
    };
#undef n

    cmsStage *mpe =  cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL);

    if (mpe == NULL) return mpe;
    mpe ->Implements = cmsSigXYZ2FloatPCS;
    return mpe;
}

cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID)
{
    static const cmsFloat64Number a1[] = {
        100.0, 0, 0,
        0, 255.0, 0,
        0, 0, 255.0
    };

    static const cmsFloat64Number o1[] = {
        0,
        -128.0,
        -128.0
    };

    cmsStage *mpe =  cmsStageAllocMatrix(ContextID, 3, 3, a1, o1);
    if (mpe == NULL) return mpe;
    mpe ->Implements = cmsSigFloatPCS2Lab;
    return mpe;
}

cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID)
{
#define n (65535.0/32768.0)

    static const cmsFloat64Number a1[] = {
        n, 0, 0,
        0, n, 0,
        0, 0, n
    };
#undef n

    cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL);
    if (mpe == NULL) return mpe;
    mpe ->Implements = cmsSigFloatPCS2XYZ;
    return mpe;
}



// ********************************************************************************
// Type cmsSigXYZ2LabElemType
// ********************************************************************************

static
void EvaluateXYZ2Lab(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
{
    cmsCIELab Lab;
    cmsCIEXYZ XYZ;
    const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ;

    // From 0..1.0 to XYZ

    XYZ.X = In[0] * XYZadj;
    XYZ.Y = In[1] * XYZadj;
    XYZ.Z = In[2] * XYZadj;

    cmsXYZ2Lab(NULL, &Lab, &XYZ);

    // From V4 Lab to 0..1.0

    Out[0] = (cmsFloat32Number) (Lab.L / 100.0);
    Out[1] = (cmsFloat32Number) ((Lab.a + 128.0) / 255.0);
    Out[2] = (cmsFloat32Number) ((Lab.b + 128.0) / 255.0);
    return;

    cmsUNUSED_PARAMETER(mpe);
}

cmsStage* _cmsStageAllocXYZ2Lab(cmsContext ContextID)
{
    return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL);

}

// ********************************************************************************

// For v4, S-Shaped curves are placed in a/b axis to increase resolution near gray

cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID)
{
    cmsToneCurve* LabTable[3];
    cmsFloat64Number Params[1] =  {2.4} ;

    LabTable[0] = cmsBuildGamma(ContextID, 1.0);
    LabTable[1] = cmsBuildParametricToneCurve(ContextID, 108, Params);
    LabTable[2] = cmsBuildParametricToneCurve(ContextID, 108, Params);

    return cmsStageAllocToneCurves(ContextID, 3, LabTable);
}


// Free a single MPE
void CMSEXPORT cmsStageFree(cmsStage* mpe)
{
    if (mpe ->FreePtr)
        mpe ->FreePtr(mpe);

    _cmsFree(mpe ->ContextID, mpe);
}


cmsUInt32Number  CMSEXPORT cmsStageInputChannels(const cmsStage* mpe)
{
    return mpe ->InputChannels;
}

cmsUInt32Number  CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe)
{
    return mpe ->OutputChannels;
}

cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe)
{
    return mpe -> Type;
}

void* CMSEXPORT cmsStageData(const cmsStage* mpe)
{
    return mpe -> Data;
}

cmsStage*  CMSEXPORT cmsStageNext(const cmsStage* mpe)
{
    return mpe -> Next;
}


// Duplicates an MPE
cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe)
{
    cmsStage* NewMPE;

    if (mpe == NULL) return NULL;
    NewMPE = _cmsStageAllocPlaceholder(mpe ->ContextID,
                                     mpe ->Type,
                                     mpe ->InputChannels,
                                     mpe ->OutputChannels,
                                     mpe ->EvalPtr,
                                     mpe ->DupElemPtr,
                                     mpe ->FreePtr,
                                     NULL);
    if (NewMPE == NULL) return NULL;

    NewMPE ->Implements     = mpe ->Implements;

    if (mpe ->DupElemPtr)
        NewMPE ->Data       = mpe ->DupElemPtr(mpe);
    else
        NewMPE ->Data       = NULL;

    return NewMPE;
}


// ***********************************************************************************************************

// This function sets up the channel count

static
void BlessLUT(cmsPipeline* lut)
{
    // We can set the input/output channels only if we have elements.
    if (lut ->Elements != NULL) {

        cmsStage *First, *Last;

        First  = cmsPipelineGetPtrToFirstStage(lut);
        Last   = cmsPipelineGetPtrToLastStage(lut);

        if (First != NULL)lut ->InputChannels = First ->InputChannels;
        if (Last != NULL) lut ->OutputChannels = Last ->OutputChannels;
    }
}


// Default to evaluate the LUT on 16 bit-basis. Precision is retained.
static
void _LUTeval16(register const cmsUInt16Number In[], register cmsUInt16Number Out[],  register const void* D)
{
    cmsPipeline* lut = (cmsPipeline*) D;
    cmsStage *mpe;
    cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS];
    int Phase = 0, NextPhase;

    From16ToFloat(In, &Storage[Phase][0], lut ->InputChannels);

    for (mpe = lut ->Elements;
         mpe != NULL;
         mpe = mpe ->Next) {

             NextPhase = Phase ^ 1;
             mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe);
             Phase = NextPhase;
    }


    FromFloatTo16(&Storage[Phase][0], Out, lut ->OutputChannels);
}



// Does evaluate the LUT on cmsFloat32Number-basis.
static
void _LUTevalFloat(register const cmsFloat32Number In[], register cmsFloat32Number Out[], const void* D)
{
    cmsPipeline* lut = (cmsPipeline*) D;
    cmsStage *mpe;
    cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS];
    int Phase = 0, NextPhase;

    memmove(&Storage[Phase][0], In, lut ->InputChannels  * sizeof(cmsFloat32Number));

    for (mpe = lut ->Elements;
         mpe != NULL;
         mpe = mpe ->Next) {

              NextPhase = Phase ^ 1;
              mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe);
              Phase = NextPhase;
    }

    memmove(Out, &Storage[Phase][0], lut ->OutputChannels * sizeof(cmsFloat32Number));
}




// LUT Creation & Destruction

cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels)
{
       cmsPipeline* NewLUT;

       if (InputChannels >= cmsMAXCHANNELS ||
           OutputChannels >= cmsMAXCHANNELS) return NULL;

       NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline));
       if (NewLUT == NULL) return NULL;


       NewLUT -> InputChannels  = InputChannels;
       NewLUT -> OutputChannels = OutputChannels;

       NewLUT ->Eval16Fn    = _LUTeval16;
       NewLUT ->EvalFloatFn = _LUTevalFloat;
       NewLUT ->DupDataFn   = NULL;
       NewLUT ->FreeDataFn  = NULL;
       NewLUT ->Data        = NewLUT;
       NewLUT ->ContextID   = ContextID;

       BlessLUT(NewLUT);

       return NewLUT;
}

cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut)
{
    _cmsAssert(lut != NULL);
    return lut ->ContextID;
}

cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut)
{
    _cmsAssert(lut != NULL);
    return lut ->InputChannels;
}

cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut)
{
    _cmsAssert(lut != NULL);
    return lut ->OutputChannels;
}

// Free a profile elements LUT
void CMSEXPORT cmsPipelineFree(cmsPipeline* lut)
{
    cmsStage *mpe, *Next;

    if (lut == NULL) return;

    for (mpe = lut ->Elements;
        mpe != NULL;
        mpe = Next) {

            Next = mpe ->Next;
            cmsStageFree(mpe);
    }

    if (lut ->FreeDataFn) lut ->FreeDataFn(lut ->ContextID, lut ->Data);

    _cmsFree(lut ->ContextID, lut);
}


// Default to evaluate the LUT on 16 bit-basis.
void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[],  const cmsPipeline* lut)
{
    _cmsAssert(lut != NULL);
    lut ->Eval16Fn(In, Out, lut->Data);
}


// Does evaluate the LUT on cmsFloat32Number-basis.
void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut)
{
    _cmsAssert(lut != NULL);
    lut ->EvalFloatFn(In, Out, lut);
}



// Duplicates a LUT
cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut)
{
    cmsPipeline* NewLUT;
    cmsStage *NewMPE, *Anterior = NULL, *mpe;
    cmsBool  First = TRUE;

    if (lut == NULL) return NULL;

    NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels);
    if (NewLUT == NULL) return NULL;

    for (mpe = lut ->Elements;
         mpe != NULL;
         mpe = mpe ->Next) {

             NewMPE = cmsStageDup(mpe);

             if (NewMPE == NULL) {
                 cmsPipelineFree(NewLUT);
                 return NULL;
             }

             if (First) {
                 NewLUT ->Elements = NewMPE;
                 First = FALSE;
             }
             else {
                Anterior ->Next = NewMPE;
             }

            Anterior = NewMPE;
    }

    NewLUT ->Eval16Fn    = lut ->Eval16Fn;
    NewLUT ->EvalFloatFn = lut ->EvalFloatFn;
    NewLUT ->DupDataFn   = lut ->DupDataFn;
    NewLUT ->FreeDataFn  = lut ->FreeDataFn;

    if (NewLUT ->DupDataFn != NULL)
        NewLUT ->Data = NewLUT ->DupDataFn(lut ->ContextID, lut->Data);


    NewLUT ->SaveAs8Bits    = lut ->SaveAs8Bits;

    BlessLUT(NewLUT);
    return NewLUT;
}


void CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe)
{
    cmsStage* Anterior = NULL, *pt;

    _cmsAssert(lut != NULL);
    _cmsAssert(mpe != NULL);

    switch (loc) {

        case cmsAT_BEGIN:
            mpe ->Next = lut ->Elements;
            lut ->Elements = mpe;
            break;

        case cmsAT_END:

            if (lut ->Elements == NULL)
                lut ->Elements = mpe;
            else {

                for (pt = lut ->Elements;
                     pt != NULL;
                     pt = pt -> Next) Anterior = pt;

                Anterior ->Next = mpe;
                mpe ->Next = NULL;
            }
            break;
        default:;
    }

    BlessLUT(lut);
}

// Unlink an element and return the pointer to it
void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe)
{
    cmsStage *Anterior, *pt, *Last;
    cmsStage *Unlinked = NULL;


    // If empty LUT, there is nothing to remove
    if (lut ->Elements == NULL) {
        if (mpe) *mpe = NULL;
        return;
    }

    // On depending on the strategy...
    switch (loc) {

        case cmsAT_BEGIN:
            {
                cmsStage* elem = lut ->Elements;

                lut ->Elements = elem -> Next;
                elem ->Next = NULL;
                Unlinked = elem;

            }
            break;

        case cmsAT_END:
            Anterior = Last = NULL;
            for (pt = lut ->Elements;
                pt != NULL;
                pt = pt -> Next) {
                    Anterior = Last;
                    Last = pt;
            }

            Unlinked = Last;  // Next already points to NULL

            // Truncate the chain
            if (Anterior)
                Anterior ->Next = NULL;
            else
                lut ->Elements = NULL;
            break;
        default:;
    }

    if (mpe)
        *mpe = Unlinked;
    else
        cmsStageFree(Unlinked);

    BlessLUT(lut);
}


// Concatenate two LUT into a new single one
cmsBool  CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2)
{
    cmsStage* mpe, *NewMPE;

    // If both LUTS does not have elements, we need to inherit
    // the number of channels
    if (l1 ->Elements == NULL && l2 ->Elements == NULL) {
        l1 ->InputChannels  = l2 ->InputChannels;
        l1 ->OutputChannels = l2 ->OutputChannels;
    }

    // Cat second
    for (mpe = l2 ->Elements;
         mpe != NULL;
         mpe = mpe ->Next) {

            // We have to dup each element
             NewMPE = cmsStageDup(mpe);

             if (NewMPE == NULL) {
                 return FALSE;
             }

             cmsPipelineInsertStage(l1, cmsAT_END, NewMPE);
    }

  BlessLUT(l1);
  return TRUE;
}


cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On)
{
    cmsBool Anterior = lut ->SaveAs8Bits;

    lut ->SaveAs8Bits = On;
    return Anterior;
}


cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut)
{
    return lut ->Elements;
}

cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut)
{
    cmsStage *mpe, *Anterior = NULL;

    for (mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next)
        Anterior = mpe;

    return Anterior;
}

cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut)
{
    cmsStage *mpe;
    cmsUInt32Number n;

    for (n=0, mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next)
            n++;

    return n;
}

// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional
// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality.
void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut,
                                        _cmsOPTeval16Fn Eval16,
                                        void* PrivateData,
                                        _cmsFreeUserDataFn FreePrivateDataFn,
                                        _cmsDupUserDataFn  DupPrivateDataFn)
{

    Lut ->Eval16Fn = Eval16;
    Lut ->DupDataFn = DupPrivateDataFn;
    Lut ->FreeDataFn = FreePrivateDataFn;
    Lut ->Data = PrivateData;
}


// ----------------------------------------------------------- Reverse interpolation
// Here's how it goes. The derivative Df(x) of the function f is the linear
// transformation that best approximates f near the point x. It can be represented
// by a matrix A whose entries are the partial derivatives of the components of f
// with respect to all the coordinates. This is know as the Jacobian
//
// The best linear approximation to f is given by the matrix equation:
//
// y-y0 = A (x-x0)
//
// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this
// linear approximation will give a "better guess" for the zero of f. Thus let y=0,
// and since y0=f(x0) one can solve the above equation for x. This leads to the
// Newton's method formula:
//
// xn+1 = xn - A-1 f(xn)
//
// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the
// fashion described above. Iterating this will give better and better approximations
// if you have a "good enough" initial guess.


#define JACOBIAN_EPSILON            0.001f
#define INVERSION_MAX_ITERATIONS    30

// Increment with reflexion on boundary
static
void IncDelta(cmsFloat32Number *Val)
{
    if (*Val < (1.0 - JACOBIAN_EPSILON))

        *Val += JACOBIAN_EPSILON;

    else
        *Val -= JACOBIAN_EPSILON;

}



// Euclidean distance between two vectors of n elements each one
static
cmsFloat32Number EuclideanDistance(cmsFloat32Number a[], cmsFloat32Number b[], int n)
{
    cmsFloat32Number sum = 0;
    int i;

    for (i=0; i < n; i++) {
        cmsFloat32Number dif = b[i] - a[i];
        sum +=  dif * dif;
    }

    return sqrtf(sum);
}


// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT. Uses Newton method
//
// x1 <- x - [J(x)]^-1 * f(x)
//
// lut: The LUT on where to do the search
// Target: LabK, 3 values of Lab plus destination K which is fixed
// Result: The obtained CMYK
// Hint:   Location where begin the search

cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[],
                                              cmsFloat32Number Result[],
                                              cmsFloat32Number Hint[],
                                              const cmsPipeline* lut)
{
    cmsUInt32Number  i, j;
    cmsFloat64Number  error, LastError = 1E20;
    cmsFloat32Number  fx[4], x[4], xd[4], fxd[4];
    cmsVEC3 tmp, tmp2;
    cmsMAT3 Jacobian;
    cmsFloat64Number LastResult[4];


    // Only 3->3 and 4->3 are supported
    if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE;
    if (lut ->OutputChannels != 3) return FALSE;

    // Mark result of -1
    LastResult[0] = LastResult[1] = LastResult[2] = -1.0f;

    // Take the hint as starting point if specified
    if (Hint == NULL) {

        // Begin at any point, we choose 1/3 of CMY axis
        x[0] = x[1] = x[2] = 0.3f;
    }
    else {

        // Only copy 3 channels from hint...
        for (j=0; j < 3; j++)
            x[j] = Hint[j];
    }

    // If Lut is 4-dimensions, then grab target[3], which is fixed
    if (lut ->InputChannels == 4) {
        x[3] = Target[3];
    }
    else x[3] = 0; // To keep lint happy


    // Iterate
    for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) {

        // Get beginning fx
        cmsPipelineEvalFloat(x, fx, lut);

        // Compute error
        error = EuclideanDistance(fx, Target, 3);

        // If not convergent, return last safe value
        if (error >= LastError)
            break;

        // Keep latest values
        LastError     = error;
        for (j=0; j < lut ->InputChannels; j++)
                Result[j] = x[j];

        // Found an exact match?
        if (error <= 0)
            break;

        // Obtain slope (the Jacobian)
        for (j = 0; j < 3; j++) {

            xd[0] = x[0];
            xd[1] = x[1];
            xd[2] = x[2];
            xd[3] = x[3];  // Keep fixed channel

            IncDelta(&xd[j]);

            cmsPipelineEvalFloat(xd, fxd, lut);

            Jacobian.v[0].n[j] = ((fxd[0] - fx[0]) / JACOBIAN_EPSILON);
            Jacobian.v[1].n[j] = ((fxd[1] - fx[1]) / JACOBIAN_EPSILON);
            Jacobian.v[2].n[j] = ((fxd[2] - fx[2]) / JACOBIAN_EPSILON);
        }

        // Solve system
        tmp2.n[0] = fx[0] - Target[0];
        tmp2.n[1] = fx[1] - Target[1];
        tmp2.n[2] = fx[2] - Target[2];

        if (!_cmsMAT3solve(&tmp, &Jacobian, &tmp2))
            return FALSE;

        // Move our guess
        x[0] -= (cmsFloat32Number) tmp.n[0];
        x[1] -= (cmsFloat32Number) tmp.n[1];
        x[2] -= (cmsFloat32Number) tmp.n[2];

        // Some clipping....
        for (j=0; j < 3; j++) {
            if (x[j] < 0) x[j] = 0;
            else
                if (x[j] > 1.0) x[j] = 1.0;
        }
    }

    return TRUE;
}


Other Java examples (source code examples)

Here is a short list of links related to this Java cmslut.c source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.