Path: blob/master/src/java.desktop/share/native/liblcms/cmslut.c
41149 views
/*1* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.2*3* This code is free software; you can redistribute it and/or modify it4* under the terms of the GNU General Public License version 2 only, as5* published by the Free Software Foundation. Oracle designates this6* particular file as subject to the "Classpath" exception as provided7* by Oracle in the LICENSE file that accompanied this code.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*/2324// This file is available under and governed by the GNU General Public25// License version 2 only, as published by the Free Software Foundation.26// However, the following notice accompanied the original version of this27// file:28//29//---------------------------------------------------------------------------------30//31// Little Color Management System32// Copyright (c) 1998-2020 Marti Maria Saguer33//34// Permission is hereby granted, free of charge, to any person obtaining35// a copy of this software and associated documentation files (the "Software"),36// to deal in the Software without restriction, including without limitation37// the rights to use, copy, modify, merge, publish, distribute, sublicense,38// and/or sell copies of the Software, and to permit persons to whom the Software39// is furnished to do so, subject to the following conditions:40//41// The above copyright notice and this permission notice shall be included in42// all copies or substantial portions of the Software.43//44// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,45// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO46// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND47// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE48// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION49// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION50// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.51//52//---------------------------------------------------------------------------------53//5455#include "lcms2_internal.h"565758// Allocates an empty multi profile element59cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID,60cmsStageSignature Type,61cmsUInt32Number InputChannels,62cmsUInt32Number OutputChannels,63_cmsStageEvalFn EvalPtr,64_cmsStageDupElemFn DupElemPtr,65_cmsStageFreeElemFn FreePtr,66void* Data)67{68cmsStage* ph = (cmsStage*) _cmsMallocZero(ContextID, sizeof(cmsStage));6970if (ph == NULL) return NULL;717273ph ->ContextID = ContextID;7475ph ->Type = Type;76ph ->Implements = Type; // By default, no clue on what is implementing7778ph ->InputChannels = InputChannels;79ph ->OutputChannels = OutputChannels;80ph ->EvalPtr = EvalPtr;81ph ->DupElemPtr = DupElemPtr;82ph ->FreePtr = FreePtr;83ph ->Data = Data;8485return ph;86}878889static90void EvaluateIdentity(const cmsFloat32Number In[],91cmsFloat32Number Out[],92const cmsStage *mpe)93{94memmove(Out, In, mpe ->InputChannels * sizeof(cmsFloat32Number));95}969798cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels)99{100return _cmsStageAllocPlaceholder(ContextID,101cmsSigIdentityElemType,102nChannels, nChannels,103EvaluateIdentity,104NULL,105NULL,106NULL);107}108109// Conversion functions. From floating point to 16 bits110static111void FromFloatTo16(const cmsFloat32Number In[], cmsUInt16Number Out[], cmsUInt32Number n)112{113cmsUInt32Number i;114115for (i=0; i < n; i++) {116Out[i] = _cmsQuickSaturateWord(In[i] * 65535.0);117}118}119120// From 16 bits to floating point121static122void From16ToFloat(const cmsUInt16Number In[], cmsFloat32Number Out[], cmsUInt32Number n)123{124cmsUInt32Number i;125126for (i=0; i < n; i++) {127Out[i] = (cmsFloat32Number) In[i] / 65535.0F;128}129}130131132// This function is quite useful to analyze the structure of a LUT and retrieve the MPE elements133// that conform the LUT. It should be called with the LUT, the number of expected elements and134// then a list of expected types followed with a list of cmsFloat64Number pointers to MPE elements. If135// the function founds a match with current pipeline, it fills the pointers and returns TRUE136// if not, returns FALSE without touching anything. Setting pointers to NULL does bypass137// the storage process.138cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...)139{140va_list args;141cmsUInt32Number i;142cmsStage* mpe;143cmsStageSignature Type;144void** ElemPtr;145146// Make sure same number of elements147if (cmsPipelineStageCount(Lut) != n) return FALSE;148149va_start(args, n);150151// Iterate across asked types152mpe = Lut ->Elements;153for (i=0; i < n; i++) {154155// Get asked type. cmsStageSignature is promoted to int by compiler156Type = (cmsStageSignature)va_arg(args, int);157if (mpe ->Type != Type) {158159va_end(args); // Mismatch. We are done.160return FALSE;161}162mpe = mpe ->Next;163}164165// Found a combination, fill pointers if not NULL166mpe = Lut ->Elements;167for (i=0; i < n; i++) {168169ElemPtr = va_arg(args, void**);170if (ElemPtr != NULL)171*ElemPtr = mpe;172173mpe = mpe ->Next;174}175176va_end(args);177return TRUE;178}179180// Below there are implementations for several types of elements. Each type may be implemented by a181// evaluation function, a duplication function, a function to free resources and a constructor.182183// *************************************************************************************************184// Type cmsSigCurveSetElemType (curves)185// *************************************************************************************************186187cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe)188{189_cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data;190191return Data ->TheCurves;192}193194static195void EvaluateCurves(const cmsFloat32Number In[],196cmsFloat32Number Out[],197const cmsStage *mpe)198{199_cmsStageToneCurvesData* Data;200cmsUInt32Number i;201202_cmsAssert(mpe != NULL);203204Data = (_cmsStageToneCurvesData*) mpe ->Data;205if (Data == NULL) return;206207if (Data ->TheCurves == NULL) return;208209for (i=0; i < Data ->nCurves; i++) {210Out[i] = cmsEvalToneCurveFloat(Data ->TheCurves[i], In[i]);211}212}213214static215void CurveSetElemTypeFree(cmsStage* mpe)216{217_cmsStageToneCurvesData* Data;218cmsUInt32Number i;219220_cmsAssert(mpe != NULL);221222Data = (_cmsStageToneCurvesData*) mpe ->Data;223if (Data == NULL) return;224225if (Data ->TheCurves != NULL) {226for (i=0; i < Data ->nCurves; i++) {227if (Data ->TheCurves[i] != NULL)228cmsFreeToneCurve(Data ->TheCurves[i]);229}230}231_cmsFree(mpe ->ContextID, Data ->TheCurves);232_cmsFree(mpe ->ContextID, Data);233}234235236static237void* CurveSetDup(cmsStage* mpe)238{239_cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data;240_cmsStageToneCurvesData* NewElem;241cmsUInt32Number i;242243NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageToneCurvesData));244if (NewElem == NULL) return NULL;245246NewElem ->nCurves = Data ->nCurves;247NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(mpe ->ContextID, NewElem ->nCurves, sizeof(cmsToneCurve*));248249if (NewElem ->TheCurves == NULL) goto Error;250251for (i=0; i < NewElem ->nCurves; i++) {252253// Duplicate each curve. It may fail.254NewElem ->TheCurves[i] = cmsDupToneCurve(Data ->TheCurves[i]);255if (NewElem ->TheCurves[i] == NULL) goto Error;256257258}259return (void*) NewElem;260261Error:262263if (NewElem ->TheCurves != NULL) {264for (i=0; i < NewElem ->nCurves; i++) {265if (NewElem ->TheCurves[i])266cmsFreeToneCurve(NewElem ->TheCurves[i]);267}268}269_cmsFree(mpe ->ContextID, NewElem ->TheCurves);270_cmsFree(mpe ->ContextID, NewElem);271return NULL;272}273274275// Curves == NULL forces identity curves276cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[])277{278cmsUInt32Number i;279_cmsStageToneCurvesData* NewElem;280cmsStage* NewMPE;281282283NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCurveSetElemType, nChannels, nChannels,284EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL );285if (NewMPE == NULL) return NULL;286287NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData));288if (NewElem == NULL) {289cmsStageFree(NewMPE);290return NULL;291}292293NewMPE ->Data = (void*) NewElem;294295NewElem ->nCurves = nChannels;296NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, nChannels, sizeof(cmsToneCurve*));297if (NewElem ->TheCurves == NULL) {298cmsStageFree(NewMPE);299return NULL;300}301302for (i=0; i < nChannels; i++) {303304if (Curves == NULL) {305NewElem ->TheCurves[i] = cmsBuildGamma(ContextID, 1.0);306}307else {308NewElem ->TheCurves[i] = cmsDupToneCurve(Curves[i]);309}310311if (NewElem ->TheCurves[i] == NULL) {312cmsStageFree(NewMPE);313return NULL;314}315316}317318return NewMPE;319}320321322// Create a bunch of identity curves323cmsStage* CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels)324{325cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL);326327if (mpe == NULL) return NULL;328mpe ->Implements = cmsSigIdentityElemType;329return mpe;330}331332333// *************************************************************************************************334// Type cmsSigMatrixElemType (Matrices)335// *************************************************************************************************336337338// Special care should be taken here because precision loss. A temporary cmsFloat64Number buffer is being used339static340void EvaluateMatrix(const cmsFloat32Number In[],341cmsFloat32Number Out[],342const cmsStage *mpe)343{344cmsUInt32Number i, j;345_cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;346cmsFloat64Number Tmp;347348// Input is already in 0..1.0 notation349for (i=0; i < mpe ->OutputChannels; i++) {350351Tmp = 0;352for (j=0; j < mpe->InputChannels; j++) {353Tmp += In[j] * Data->Double[i*mpe->InputChannels + j];354}355356if (Data ->Offset != NULL)357Tmp += Data->Offset[i];358359Out[i] = (cmsFloat32Number) Tmp;360}361362363// Output in 0..1.0 domain364}365366367// Duplicate a yet-existing matrix element368static369void* MatrixElemDup(cmsStage* mpe)370{371_cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;372_cmsStageMatrixData* NewElem;373cmsUInt32Number sz;374375NewElem = (_cmsStageMatrixData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageMatrixData));376if (NewElem == NULL) return NULL;377378sz = mpe ->InputChannels * mpe ->OutputChannels;379380NewElem ->Double = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, Data ->Double, sz * sizeof(cmsFloat64Number)) ;381382if (Data ->Offset)383NewElem ->Offset = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID,384Data ->Offset, mpe -> OutputChannels * sizeof(cmsFloat64Number)) ;385386return (void*) NewElem;387}388389390static391void MatrixElemTypeFree(cmsStage* mpe)392{393_cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;394if (Data == NULL)395return;396if (Data ->Double)397_cmsFree(mpe ->ContextID, Data ->Double);398399if (Data ->Offset)400_cmsFree(mpe ->ContextID, Data ->Offset);401402_cmsFree(mpe ->ContextID, mpe ->Data);403}404405406407cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols,408const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset)409{410cmsUInt32Number i, n;411_cmsStageMatrixData* NewElem;412cmsStage* NewMPE;413414n = Rows * Cols;415416// Check for overflow417if (n == 0) return NULL;418if (n >= UINT_MAX / Cols) return NULL;419if (n >= UINT_MAX / Rows) return NULL;420if (n < Rows || n < Cols) return NULL;421422NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigMatrixElemType, Cols, Rows,423EvaluateMatrix, MatrixElemDup, MatrixElemTypeFree, NULL );424if (NewMPE == NULL) return NULL;425426427NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData));428if (NewElem == NULL) goto Error;429NewMPE->Data = (void*)NewElem;430431NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number));432if (NewElem->Double == NULL) goto Error;433434for (i=0; i < n; i++) {435NewElem ->Double[i] = Matrix[i];436}437438if (Offset != NULL) {439440NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number));441if (NewElem->Offset == NULL) goto Error;442443for (i=0; i < Rows; i++) {444NewElem ->Offset[i] = Offset[i];445}446}447448return NewMPE;449450Error:451cmsStageFree(NewMPE);452return NULL;453}454455456// *************************************************************************************************457// Type cmsSigCLutElemType458// *************************************************************************************************459460461// Evaluate in true floating point462static463void EvaluateCLUTfloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)464{465_cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;466467Data -> Params ->Interpolation.LerpFloat(In, Out, Data->Params);468}469470471// Convert to 16 bits, evaluate, and back to floating point472static473void EvaluateCLUTfloatIn16(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)474{475_cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;476cmsUInt16Number In16[MAX_STAGE_CHANNELS], Out16[MAX_STAGE_CHANNELS];477478_cmsAssert(mpe ->InputChannels <= MAX_STAGE_CHANNELS);479_cmsAssert(mpe ->OutputChannels <= MAX_STAGE_CHANNELS);480481FromFloatTo16(In, In16, mpe ->InputChannels);482Data -> Params ->Interpolation.Lerp16(In16, Out16, Data->Params);483From16ToFloat(Out16, Out, mpe ->OutputChannels);484}485486487// Given an hypercube of b dimensions, with Dims[] number of nodes by dimension, calculate the total amount of nodes488static489cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b)490{491cmsUInt32Number rv, dim;492493_cmsAssert(Dims != NULL);494495for (rv = 1; b > 0; b--) {496497dim = Dims[b-1];498if (dim == 0) return 0; // Error499500rv *= dim;501502// Check for overflow503if (rv > UINT_MAX / dim) return 0;504}505506return rv;507}508509static510void* CLUTElemDup(cmsStage* mpe)511{512_cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;513_cmsStageCLutData* NewElem;514515516NewElem = (_cmsStageCLutData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageCLutData));517if (NewElem == NULL) return NULL;518519NewElem ->nEntries = Data ->nEntries;520NewElem ->HasFloatValues = Data ->HasFloatValues;521522if (Data ->Tab.T) {523524if (Data ->HasFloatValues) {525NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number));526if (NewElem ->Tab.TFloat == NULL)527goto Error;528} else {529NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number));530if (NewElem ->Tab.T == NULL)531goto Error;532}533}534535NewElem ->Params = _cmsComputeInterpParamsEx(mpe ->ContextID,536Data ->Params ->nSamples,537Data ->Params ->nInputs,538Data ->Params ->nOutputs,539NewElem ->Tab.T,540Data ->Params ->dwFlags);541if (NewElem->Params != NULL)542return (void*) NewElem;543Error:544if (NewElem->Tab.T)545// This works for both types546_cmsFree(mpe ->ContextID, NewElem -> Tab.T);547_cmsFree(mpe ->ContextID, NewElem);548return NULL;549}550551552static553void CLutElemTypeFree(cmsStage* mpe)554{555556_cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;557558// Already empty559if (Data == NULL) return;560561// This works for both types562if (Data -> Tab.T)563_cmsFree(mpe ->ContextID, Data -> Tab.T);564565_cmsFreeInterpParams(Data ->Params);566_cmsFree(mpe ->ContextID, mpe ->Data);567}568569570// Allocates a 16-bit multidimensional CLUT. This is evaluated at 16-bit precision. Table may have different571// granularity on each dimension.572cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID,573const cmsUInt32Number clutPoints[],574cmsUInt32Number inputChan,575cmsUInt32Number outputChan,576const cmsUInt16Number* Table)577{578cmsUInt32Number i, n;579_cmsStageCLutData* NewElem;580cmsStage* NewMPE;581582_cmsAssert(clutPoints != NULL);583584if (inputChan > MAX_INPUT_DIMENSIONS) {585cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS);586return NULL;587}588589NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,590EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL );591592if (NewMPE == NULL) return NULL;593594NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));595if (NewElem == NULL) {596cmsStageFree(NewMPE);597return NULL;598}599600NewMPE ->Data = (void*) NewElem;601602NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan);603NewElem -> HasFloatValues = FALSE;604605if (n == 0) {606cmsStageFree(NewMPE);607return NULL;608}609610611NewElem ->Tab.T = (cmsUInt16Number*) _cmsCalloc(ContextID, n, sizeof(cmsUInt16Number));612if (NewElem ->Tab.T == NULL) {613cmsStageFree(NewMPE);614return NULL;615}616617if (Table != NULL) {618for (i=0; i < n; i++) {619NewElem ->Tab.T[i] = Table[i];620}621}622623NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.T, CMS_LERP_FLAGS_16BITS);624if (NewElem ->Params == NULL) {625cmsStageFree(NewMPE);626return NULL;627}628629return NewMPE;630}631632cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID,633cmsUInt32Number nGridPoints,634cmsUInt32Number inputChan,635cmsUInt32Number outputChan,636const cmsUInt16Number* Table)637{638cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];639int i;640641// Our resulting LUT would be same gridpoints on all dimensions642for (i=0; i < MAX_INPUT_DIMENSIONS; i++)643Dimensions[i] = nGridPoints;644645return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table);646}647648649cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID,650cmsUInt32Number nGridPoints,651cmsUInt32Number inputChan,652cmsUInt32Number outputChan,653const cmsFloat32Number* Table)654{655cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];656int i;657658// Our resulting LUT would be same gridpoints on all dimensions659for (i=0; i < MAX_INPUT_DIMENSIONS; i++)660Dimensions[i] = nGridPoints;661662return cmsStageAllocCLutFloatGranular(ContextID, Dimensions, inputChan, outputChan, Table);663}664665666667cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table)668{669cmsUInt32Number i, n;670_cmsStageCLutData* NewElem;671cmsStage* NewMPE;672673_cmsAssert(clutPoints != NULL);674675if (inputChan > MAX_INPUT_DIMENSIONS) {676cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS);677return NULL;678}679680NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,681EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL);682if (NewMPE == NULL) return NULL;683684685NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));686if (NewElem == NULL) {687cmsStageFree(NewMPE);688return NULL;689}690691NewMPE ->Data = (void*) NewElem;692693// There is a potential integer overflow on conputing n and nEntries.694NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan);695NewElem -> HasFloatValues = TRUE;696697if (n == 0) {698cmsStageFree(NewMPE);699return NULL;700}701702NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat32Number));703if (NewElem ->Tab.TFloat == NULL) {704cmsStageFree(NewMPE);705return NULL;706}707708if (Table != NULL) {709for (i=0; i < n; i++) {710NewElem ->Tab.TFloat[i] = Table[i];711}712}713714NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT);715if (NewElem ->Params == NULL) {716cmsStageFree(NewMPE);717return NULL;718}719720return NewMPE;721}722723724static725int IdentitySampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void * Cargo)726{727int nChan = *(int*) Cargo;728int i;729730for (i=0; i < nChan; i++)731Out[i] = In[i];732733return 1;734}735736// Creates an MPE that just copies input to output737cmsStage* CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan)738{739cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];740cmsStage* mpe ;741int i;742743for (i=0; i < MAX_INPUT_DIMENSIONS; i++)744Dimensions[i] = 2;745746mpe = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, nChan, nChan, NULL);747if (mpe == NULL) return NULL;748749if (!cmsStageSampleCLut16bit(mpe, IdentitySampler, &nChan, 0)) {750cmsStageFree(mpe);751return NULL;752}753754mpe ->Implements = cmsSigIdentityElemType;755return mpe;756}757758759760// Quantize a value 0 <= i < MaxSamples to 0..0xffff761cmsUInt16Number CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples)762{763cmsFloat64Number x;764765x = ((cmsFloat64Number) i * 65535.) / (cmsFloat64Number) (MaxSamples - 1);766return _cmsQuickSaturateWord(x);767}768769770// This routine does a sweep on whole input space, and calls its callback771// function on knots. returns TRUE if all ok, FALSE otherwise.772cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags)773{774int i, t, index, rest;775cmsUInt32Number nTotalPoints;776cmsUInt32Number nInputs, nOutputs;777cmsUInt32Number* nSamples;778cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];779_cmsStageCLutData* clut;780781if (mpe == NULL) return FALSE;782783clut = (_cmsStageCLutData*) mpe->Data;784785if (clut == NULL) return FALSE;786787nSamples = clut->Params ->nSamples;788nInputs = clut->Params ->nInputs;789nOutputs = clut->Params ->nOutputs;790791if (nInputs <= 0) return FALSE;792if (nOutputs <= 0) return FALSE;793if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE;794if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;795796memset(In, 0, sizeof(In));797memset(Out, 0, sizeof(Out));798799nTotalPoints = CubeSize(nSamples, nInputs);800if (nTotalPoints == 0) return FALSE;801802index = 0;803for (i = 0; i < (int) nTotalPoints; i++) {804805rest = i;806for (t = (int)nInputs - 1; t >= 0; --t) {807808cmsUInt32Number Colorant = rest % nSamples[t];809810rest /= nSamples[t];811812In[t] = _cmsQuantizeVal(Colorant, nSamples[t]);813}814815if (clut ->Tab.T != NULL) {816for (t = 0; t < (int)nOutputs; t++)817Out[t] = clut->Tab.T[index + t];818}819820if (!Sampler(In, Out, Cargo))821return FALSE;822823if (!(dwFlags & SAMPLER_INSPECT)) {824825if (clut ->Tab.T != NULL) {826for (t=0; t < (int) nOutputs; t++)827clut->Tab.T[index + t] = Out[t];828}829}830831index += nOutputs;832}833834return TRUE;835}836837// Same as anterior, but for floating point838cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags)839{840int i, t, index, rest;841cmsUInt32Number nTotalPoints;842cmsUInt32Number nInputs, nOutputs;843cmsUInt32Number* nSamples;844cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];845_cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data;846847nSamples = clut->Params ->nSamples;848nInputs = clut->Params ->nInputs;849nOutputs = clut->Params ->nOutputs;850851if (nInputs <= 0) return FALSE;852if (nOutputs <= 0) return FALSE;853if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE;854if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;855856nTotalPoints = CubeSize(nSamples, nInputs);857if (nTotalPoints == 0) return FALSE;858859index = 0;860for (i = 0; i < (int)nTotalPoints; i++) {861862rest = i;863for (t = (int) nInputs-1; t >=0; --t) {864865cmsUInt32Number Colorant = rest % nSamples[t];866867rest /= nSamples[t];868869In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, nSamples[t]) / 65535.0);870}871872if (clut ->Tab.TFloat != NULL) {873for (t=0; t < (int) nOutputs; t++)874Out[t] = clut->Tab.TFloat[index + t];875}876877if (!Sampler(In, Out, Cargo))878return FALSE;879880if (!(dwFlags & SAMPLER_INSPECT)) {881882if (clut ->Tab.TFloat != NULL) {883for (t=0; t < (int) nOutputs; t++)884clut->Tab.TFloat[index + t] = Out[t];885}886}887888index += nOutputs;889}890891return TRUE;892}893894895896// This routine does a sweep on whole input space, and calls its callback897// function on knots. returns TRUE if all ok, FALSE otherwise.898cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],899cmsSAMPLER16 Sampler, void * Cargo)900{901int i, t, rest;902cmsUInt32Number nTotalPoints;903cmsUInt16Number In[cmsMAXCHANNELS];904905if (nInputs >= cmsMAXCHANNELS) return FALSE;906907nTotalPoints = CubeSize(clutPoints, nInputs);908if (nTotalPoints == 0) return FALSE;909910for (i = 0; i < (int) nTotalPoints; i++) {911912rest = i;913for (t = (int) nInputs-1; t >=0; --t) {914915cmsUInt32Number Colorant = rest % clutPoints[t];916917rest /= clutPoints[t];918In[t] = _cmsQuantizeVal(Colorant, clutPoints[t]);919920}921922if (!Sampler(In, NULL, Cargo))923return FALSE;924}925926return TRUE;927}928929cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],930cmsSAMPLERFLOAT Sampler, void * Cargo)931{932int i, t, rest;933cmsUInt32Number nTotalPoints;934cmsFloat32Number In[cmsMAXCHANNELS];935936if (nInputs >= cmsMAXCHANNELS) return FALSE;937938nTotalPoints = CubeSize(clutPoints, nInputs);939if (nTotalPoints == 0) return FALSE;940941for (i = 0; i < (int) nTotalPoints; i++) {942943rest = i;944for (t = (int) nInputs-1; t >=0; --t) {945946cmsUInt32Number Colorant = rest % clutPoints[t];947948rest /= clutPoints[t];949In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, clutPoints[t]) / 65535.0);950951}952953if (!Sampler(In, NULL, Cargo))954return FALSE;955}956957return TRUE;958}959960// ********************************************************************************961// Type cmsSigLab2XYZElemType962// ********************************************************************************963964965static966void EvaluateLab2XYZ(const cmsFloat32Number In[],967cmsFloat32Number Out[],968const cmsStage *mpe)969{970cmsCIELab Lab;971cmsCIEXYZ XYZ;972const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ;973974// V4 rules975Lab.L = In[0] * 100.0;976Lab.a = In[1] * 255.0 - 128.0;977Lab.b = In[2] * 255.0 - 128.0;978979cmsLab2XYZ(NULL, &XYZ, &Lab);980981// From XYZ, range 0..19997 to 0..1.0, note that 1.99997 comes from 0xffff982// encoded as 1.15 fixed point, so 1 + (32767.0 / 32768.0)983984Out[0] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.X / XYZadj);985Out[1] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Y / XYZadj);986Out[2] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Z / XYZadj);987return;988989cmsUNUSED_PARAMETER(mpe);990}991992993// No dup or free routines needed, as the structure has no pointers in it.994cmsStage* CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID)995{996return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL);997}998999// ********************************************************************************10001001// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable1002// number of gridpoints that would make exact match. However, a prelinearization1003// of 258 entries, would map 0xFF00 exactly on entry 257, and this is good to avoid scum dot.1004// Almost all what we need but unfortunately, the rest of entries should be scaled by1005// (255*257/256) and this is not exact.10061007cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID)1008{1009cmsStage* mpe;1010cmsToneCurve* LabTable[3];1011int i, j;10121013LabTable[0] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL);1014LabTable[1] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL);1015LabTable[2] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL);10161017for (j=0; j < 3; j++) {10181019if (LabTable[j] == NULL) {1020cmsFreeToneCurveTriple(LabTable);1021return NULL;1022}10231024// We need to map * (0xffff / 0xff00), that's same as (257 / 256)1025// So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256);1026for (i=0; i < 257; i++) {10271028LabTable[j]->Table16[i] = (cmsUInt16Number) ((i * 0xffff + 0x80) >> 8);1029}10301031LabTable[j] ->Table16[257] = 0xffff;1032}10331034mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable);1035cmsFreeToneCurveTriple(LabTable);10361037if (mpe == NULL) return NULL;1038mpe ->Implements = cmsSigLabV2toV4;1039return mpe;1040}10411042// ********************************************************************************10431044// Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles1045cmsStage* CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID)1046{1047static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0,10480, 65535.0/65280.0, 0,10490, 0, 65535.0/65280.01050};10511052cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V2ToV4, NULL);10531054if (mpe == NULL) return mpe;1055mpe ->Implements = cmsSigLabV2toV4;1056return mpe;1057}105810591060// Reverse direction1061cmsStage* CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID)1062{1063static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0,10640, 65280.0/65535.0, 0,10650, 0, 65280.0/65535.01066};10671068cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V4ToV2, NULL);10691070if (mpe == NULL) return mpe;1071mpe ->Implements = cmsSigLabV4toV2;1072return mpe;1073}107410751076// To Lab to float. Note that the MPE gives numbers in normal Lab range1077// and we need 0..1.0 range for the formatters1078// L* : 0...100 => 0...1.0 (L* / 100)1079// ab* : -128..+127 to 0..1 ((ab* + 128) / 255)10801081cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID)1082{1083static const cmsFloat64Number a1[] = {10841.0/100.0, 0, 0,10850, 1.0/255.0, 0,10860, 0, 1.0/255.01087};10881089static const cmsFloat64Number o1[] = {10900,1091128.0/255.0,1092128.0/255.01093};10941095cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1);10961097if (mpe == NULL) return mpe;1098mpe ->Implements = cmsSigLab2FloatPCS;1099return mpe;1100}11011102// Fom XYZ to floating point PCS1103cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID)1104{1105#define n (32768.0/65535.0)1106static const cmsFloat64Number a1[] = {1107n, 0, 0,11080, n, 0,11090, 0, n1110};1111#undef n11121113cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL);11141115if (mpe == NULL) return mpe;1116mpe ->Implements = cmsSigXYZ2FloatPCS;1117return mpe;1118}11191120cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID)1121{1122static const cmsFloat64Number a1[] = {1123100.0, 0, 0,11240, 255.0, 0,11250, 0, 255.01126};11271128static const cmsFloat64Number o1[] = {11290,1130-128.0,1131-128.01132};11331134cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1);1135if (mpe == NULL) return mpe;1136mpe ->Implements = cmsSigFloatPCS2Lab;1137return mpe;1138}11391140cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID)1141{1142#define n (65535.0/32768.0)11431144static const cmsFloat64Number a1[] = {1145n, 0, 0,11460, n, 0,11470, 0, n1148};1149#undef n11501151cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL);1152if (mpe == NULL) return mpe;1153mpe ->Implements = cmsSigFloatPCS2XYZ;1154return mpe;1155}11561157// Clips values smaller than zero1158static1159void Clipper(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)1160{1161cmsUInt32Number i;1162for (i = 0; i < mpe->InputChannels; i++) {11631164cmsFloat32Number n = In[i];1165Out[i] = n < 0 ? 0 : n;1166}1167}11681169cmsStage* _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels)1170{1171return _cmsStageAllocPlaceholder(ContextID, cmsSigClipNegativesElemType,1172nChannels, nChannels, Clipper, NULL, NULL, NULL);1173}11741175// ********************************************************************************1176// Type cmsSigXYZ2LabElemType1177// ********************************************************************************11781179static1180void EvaluateXYZ2Lab(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)1181{1182cmsCIELab Lab;1183cmsCIEXYZ XYZ;1184const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ;11851186// From 0..1.0 to XYZ11871188XYZ.X = In[0] * XYZadj;1189XYZ.Y = In[1] * XYZadj;1190XYZ.Z = In[2] * XYZadj;11911192cmsXYZ2Lab(NULL, &Lab, &XYZ);11931194// From V4 Lab to 0..1.011951196Out[0] = (cmsFloat32Number) (Lab.L / 100.0);1197Out[1] = (cmsFloat32Number) ((Lab.a + 128.0) / 255.0);1198Out[2] = (cmsFloat32Number) ((Lab.b + 128.0) / 255.0);1199return;12001201cmsUNUSED_PARAMETER(mpe);1202}12031204cmsStage* CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID)1205{1206return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL);12071208}12091210// ********************************************************************************12111212// For v4, S-Shaped curves are placed in a/b axis to increase resolution near gray12131214cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID)1215{1216cmsToneCurve* LabTable[3];1217cmsFloat64Number Params[1] = {2.4} ;12181219LabTable[0] = cmsBuildGamma(ContextID, 1.0);1220LabTable[1] = cmsBuildParametricToneCurve(ContextID, 108, Params);1221LabTable[2] = cmsBuildParametricToneCurve(ContextID, 108, Params);12221223return cmsStageAllocToneCurves(ContextID, 3, LabTable);1224}122512261227// Free a single MPE1228void CMSEXPORT cmsStageFree(cmsStage* mpe)1229{1230if (mpe ->FreePtr)1231mpe ->FreePtr(mpe);12321233_cmsFree(mpe ->ContextID, mpe);1234}123512361237cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe)1238{1239return mpe ->InputChannels;1240}12411242cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe)1243{1244return mpe ->OutputChannels;1245}12461247cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe)1248{1249return mpe -> Type;1250}12511252void* CMSEXPORT cmsStageData(const cmsStage* mpe)1253{1254return mpe -> Data;1255}12561257cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe)1258{1259return mpe -> Next;1260}126112621263// Duplicates an MPE1264cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe)1265{1266cmsStage* NewMPE;12671268if (mpe == NULL) return NULL;1269NewMPE = _cmsStageAllocPlaceholder(mpe ->ContextID,1270mpe ->Type,1271mpe ->InputChannels,1272mpe ->OutputChannels,1273mpe ->EvalPtr,1274mpe ->DupElemPtr,1275mpe ->FreePtr,1276NULL);1277if (NewMPE == NULL) return NULL;12781279NewMPE ->Implements = mpe ->Implements;12801281if (mpe ->DupElemPtr) {12821283NewMPE ->Data = mpe ->DupElemPtr(mpe);12841285if (NewMPE->Data == NULL) {12861287cmsStageFree(NewMPE);1288return NULL;1289}12901291} else {12921293NewMPE ->Data = NULL;1294}12951296return NewMPE;1297}129812991300// ***********************************************************************************************************13011302// This function sets up the channel count1303static1304cmsBool BlessLUT(cmsPipeline* lut)1305{1306// We can set the input/output channels only if we have elements.1307if (lut ->Elements != NULL) {13081309cmsStage* prev;1310cmsStage* next;1311cmsStage* First;1312cmsStage* Last;13131314First = cmsPipelineGetPtrToFirstStage(lut);1315Last = cmsPipelineGetPtrToLastStage(lut);13161317if (First == NULL || Last == NULL) return FALSE;13181319lut->InputChannels = First->InputChannels;1320lut->OutputChannels = Last->OutputChannels;13211322// Check chain consistency1323prev = First;1324next = prev->Next;13251326while (next != NULL)1327{1328if (next->InputChannels != prev->OutputChannels)1329return FALSE;13301331next = next->Next;1332prev = prev->Next;1333}1334}13351336return TRUE;1337}133813391340// Default to evaluate the LUT on 16 bit-basis. Precision is retained.1341static1342void _LUTeval16(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER const void* D)1343{1344cmsPipeline* lut = (cmsPipeline*) D;1345cmsStage *mpe;1346cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS];1347int Phase = 0, NextPhase;13481349From16ToFloat(In, &Storage[Phase][0], lut ->InputChannels);13501351for (mpe = lut ->Elements;1352mpe != NULL;1353mpe = mpe ->Next) {13541355NextPhase = Phase ^ 1;1356mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe);1357Phase = NextPhase;1358}135913601361FromFloatTo16(&Storage[Phase][0], Out, lut ->OutputChannels);1362}1363136413651366// Does evaluate the LUT on cmsFloat32Number-basis.1367static1368void _LUTevalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const void* D)1369{1370cmsPipeline* lut = (cmsPipeline*) D;1371cmsStage *mpe;1372cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS];1373int Phase = 0, NextPhase;13741375memmove(&Storage[Phase][0], In, lut ->InputChannels * sizeof(cmsFloat32Number));13761377for (mpe = lut ->Elements;1378mpe != NULL;1379mpe = mpe ->Next) {13801381NextPhase = Phase ^ 1;1382mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe);1383Phase = NextPhase;1384}13851386memmove(Out, &Storage[Phase][0], lut ->OutputChannels * sizeof(cmsFloat32Number));1387}138813891390// LUT Creation & Destruction1391cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels)1392{1393cmsPipeline* NewLUT;13941395// A value of zero in channels is allowed as placeholder1396if (InputChannels >= cmsMAXCHANNELS ||1397OutputChannels >= cmsMAXCHANNELS) return NULL;13981399NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline));1400if (NewLUT == NULL) return NULL;14011402NewLUT -> InputChannels = InputChannels;1403NewLUT -> OutputChannels = OutputChannels;14041405NewLUT ->Eval16Fn = _LUTeval16;1406NewLUT ->EvalFloatFn = _LUTevalFloat;1407NewLUT ->DupDataFn = NULL;1408NewLUT ->FreeDataFn = NULL;1409NewLUT ->Data = NewLUT;1410NewLUT ->ContextID = ContextID;14111412if (!BlessLUT(NewLUT))1413{1414_cmsFree(ContextID, NewLUT);1415return NULL;1416}14171418return NewLUT;1419}14201421cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut)1422{1423_cmsAssert(lut != NULL);1424return lut ->ContextID;1425}14261427cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut)1428{1429_cmsAssert(lut != NULL);1430return lut ->InputChannels;1431}14321433cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut)1434{1435_cmsAssert(lut != NULL);1436return lut ->OutputChannels;1437}14381439// Free a profile elements LUT1440void CMSEXPORT cmsPipelineFree(cmsPipeline* lut)1441{1442cmsStage *mpe, *Next;14431444if (lut == NULL) return;14451446for (mpe = lut ->Elements;1447mpe != NULL;1448mpe = Next) {14491450Next = mpe ->Next;1451cmsStageFree(mpe);1452}14531454if (lut ->FreeDataFn) lut ->FreeDataFn(lut ->ContextID, lut ->Data);14551456_cmsFree(lut ->ContextID, lut);1457}145814591460// Default to evaluate the LUT on 16 bit-basis.1461void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut)1462{1463_cmsAssert(lut != NULL);1464lut ->Eval16Fn(In, Out, lut->Data);1465}146614671468// Does evaluate the LUT on cmsFloat32Number-basis.1469void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut)1470{1471_cmsAssert(lut != NULL);1472lut ->EvalFloatFn(In, Out, lut);1473}1474147514761477// Duplicates a LUT1478cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut)1479{1480cmsPipeline* NewLUT;1481cmsStage *NewMPE, *Anterior = NULL, *mpe;1482cmsBool First = TRUE;14831484if (lut == NULL) return NULL;14851486NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels);1487if (NewLUT == NULL) return NULL;14881489for (mpe = lut ->Elements;1490mpe != NULL;1491mpe = mpe ->Next) {14921493NewMPE = cmsStageDup(mpe);14941495if (NewMPE == NULL) {1496cmsPipelineFree(NewLUT);1497return NULL;1498}14991500if (First) {1501NewLUT ->Elements = NewMPE;1502First = FALSE;1503}1504else {1505if (Anterior != NULL)1506Anterior ->Next = NewMPE;1507}15081509Anterior = NewMPE;1510}15111512NewLUT ->Eval16Fn = lut ->Eval16Fn;1513NewLUT ->EvalFloatFn = lut ->EvalFloatFn;1514NewLUT ->DupDataFn = lut ->DupDataFn;1515NewLUT ->FreeDataFn = lut ->FreeDataFn;15161517if (NewLUT ->DupDataFn != NULL)1518NewLUT ->Data = NewLUT ->DupDataFn(lut ->ContextID, lut->Data);151915201521NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits;15221523if (!BlessLUT(NewLUT))1524{1525_cmsFree(lut->ContextID, NewLUT);1526return NULL;1527}15281529return NewLUT;1530}153115321533int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe)1534{1535cmsStage* Anterior = NULL, *pt;15361537if (lut == NULL || mpe == NULL)1538return FALSE;15391540switch (loc) {15411542case cmsAT_BEGIN:1543mpe ->Next = lut ->Elements;1544lut ->Elements = mpe;1545break;15461547case cmsAT_END:15481549if (lut ->Elements == NULL)1550lut ->Elements = mpe;1551else {15521553for (pt = lut ->Elements;1554pt != NULL;1555pt = pt -> Next) Anterior = pt;15561557Anterior ->Next = mpe;1558mpe ->Next = NULL;1559}1560break;1561default:;1562return FALSE;1563}15641565return BlessLUT(lut);1566}15671568// Unlink an element and return the pointer to it1569void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe)1570{1571cmsStage *Anterior, *pt, *Last;1572cmsStage *Unlinked = NULL;157315741575// If empty LUT, there is nothing to remove1576if (lut ->Elements == NULL) {1577if (mpe) *mpe = NULL;1578return;1579}15801581// On depending on the strategy...1582switch (loc) {15831584case cmsAT_BEGIN:1585{1586cmsStage* elem = lut ->Elements;15871588lut ->Elements = elem -> Next;1589elem ->Next = NULL;1590Unlinked = elem;15911592}1593break;15941595case cmsAT_END:1596Anterior = Last = NULL;1597for (pt = lut ->Elements;1598pt != NULL;1599pt = pt -> Next) {1600Anterior = Last;1601Last = pt;1602}16031604Unlinked = Last; // Next already points to NULL16051606// Truncate the chain1607if (Anterior)1608Anterior ->Next = NULL;1609else1610lut ->Elements = NULL;1611break;1612default:;1613}16141615if (mpe)1616*mpe = Unlinked;1617else1618cmsStageFree(Unlinked);16191620// May fail, but we ignore it1621BlessLUT(lut);1622}162316241625// Concatenate two LUT into a new single one1626cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2)1627{1628cmsStage* mpe;16291630// If both LUTS does not have elements, we need to inherit1631// the number of channels1632if (l1 ->Elements == NULL && l2 ->Elements == NULL) {1633l1 ->InputChannels = l2 ->InputChannels;1634l1 ->OutputChannels = l2 ->OutputChannels;1635}16361637// Cat second1638for (mpe = l2 ->Elements;1639mpe != NULL;1640mpe = mpe ->Next) {16411642// We have to dup each element1643if (!cmsPipelineInsertStage(l1, cmsAT_END, cmsStageDup(mpe)))1644return FALSE;1645}16461647return BlessLUT(l1);1648}164916501651cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On)1652{1653cmsBool Anterior = lut ->SaveAs8Bits;16541655lut ->SaveAs8Bits = On;1656return Anterior;1657}165816591660cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut)1661{1662return lut ->Elements;1663}16641665cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut)1666{1667cmsStage *mpe, *Anterior = NULL;16681669for (mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next)1670Anterior = mpe;16711672return Anterior;1673}16741675cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut)1676{1677cmsStage *mpe;1678cmsUInt32Number n;16791680for (n=0, mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next)1681n++;16821683return n;1684}16851686// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional1687// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality.1688void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut,1689_cmsPipelineEval16Fn Eval16,1690void* PrivateData,1691_cmsFreeUserDataFn FreePrivateDataFn,1692_cmsDupUserDataFn DupPrivateDataFn)1693{16941695Lut ->Eval16Fn = Eval16;1696Lut ->DupDataFn = DupPrivateDataFn;1697Lut ->FreeDataFn = FreePrivateDataFn;1698Lut ->Data = PrivateData;1699}170017011702// ----------------------------------------------------------- Reverse interpolation1703// Here's how it goes. The derivative Df(x) of the function f is the linear1704// transformation that best approximates f near the point x. It can be represented1705// by a matrix A whose entries are the partial derivatives of the components of f1706// with respect to all the coordinates. This is know as the Jacobian1707//1708// The best linear approximation to f is given by the matrix equation:1709//1710// y-y0 = A (x-x0)1711//1712// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this1713// linear approximation will give a "better guess" for the zero of f. Thus let y=0,1714// and since y0=f(x0) one can solve the above equation for x. This leads to the1715// Newton's method formula:1716//1717// xn+1 = xn - A-1 f(xn)1718//1719// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the1720// fashion described above. Iterating this will give better and better approximations1721// if you have a "good enough" initial guess.172217231724#define JACOBIAN_EPSILON 0.001f1725#define INVERSION_MAX_ITERATIONS 3017261727// Increment with reflexion on boundary1728static1729void IncDelta(cmsFloat32Number *Val)1730{1731if (*Val < (1.0 - JACOBIAN_EPSILON))17321733*Val += JACOBIAN_EPSILON;17341735else1736*Val -= JACOBIAN_EPSILON;17371738}1739174017411742// Euclidean distance between two vectors of n elements each one1743static1744cmsFloat32Number EuclideanDistance(cmsFloat32Number a[], cmsFloat32Number b[], int n)1745{1746cmsFloat32Number sum = 0;1747int i;17481749for (i=0; i < n; i++) {1750cmsFloat32Number dif = b[i] - a[i];1751sum += dif * dif;1752}17531754return sqrtf(sum);1755}175617571758// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT. Uses Newton method1759//1760// x1 <- x - [J(x)]^-1 * f(x)1761//1762// lut: The LUT on where to do the search1763// Target: LabK, 3 values of Lab plus destination K which is fixed1764// Result: The obtained CMYK1765// Hint: Location where begin the search17661767cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[],1768cmsFloat32Number Result[],1769cmsFloat32Number Hint[],1770const cmsPipeline* lut)1771{1772cmsUInt32Number i, j;1773cmsFloat64Number error, LastError = 1E20;1774cmsFloat32Number fx[4], x[4], xd[4], fxd[4];1775cmsVEC3 tmp, tmp2;1776cmsMAT3 Jacobian;17771778// Only 3->3 and 4->3 are supported1779if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE;1780if (lut ->OutputChannels != 3) return FALSE;17811782// Take the hint as starting point if specified1783if (Hint == NULL) {17841785// Begin at any point, we choose 1/3 of CMY axis1786x[0] = x[1] = x[2] = 0.3f;1787}1788else {17891790// Only copy 3 channels from hint...1791for (j=0; j < 3; j++)1792x[j] = Hint[j];1793}17941795// If Lut is 4-dimensions, then grab target[3], which is fixed1796if (lut ->InputChannels == 4) {1797x[3] = Target[3];1798}1799else x[3] = 0; // To keep lint happy180018011802// Iterate1803for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) {18041805// Get beginning fx1806cmsPipelineEvalFloat(x, fx, lut);18071808// Compute error1809error = EuclideanDistance(fx, Target, 3);18101811// If not convergent, return last safe value1812if (error >= LastError)1813break;18141815// Keep latest values1816LastError = error;1817for (j=0; j < lut ->InputChannels; j++)1818Result[j] = x[j];18191820// Found an exact match?1821if (error <= 0)1822break;18231824// Obtain slope (the Jacobian)1825for (j = 0; j < 3; j++) {18261827xd[0] = x[0];1828xd[1] = x[1];1829xd[2] = x[2];1830xd[3] = x[3]; // Keep fixed channel18311832IncDelta(&xd[j]);18331834cmsPipelineEvalFloat(xd, fxd, lut);18351836Jacobian.v[0].n[j] = ((fxd[0] - fx[0]) / JACOBIAN_EPSILON);1837Jacobian.v[1].n[j] = ((fxd[1] - fx[1]) / JACOBIAN_EPSILON);1838Jacobian.v[2].n[j] = ((fxd[2] - fx[2]) / JACOBIAN_EPSILON);1839}18401841// Solve system1842tmp2.n[0] = fx[0] - Target[0];1843tmp2.n[1] = fx[1] - Target[1];1844tmp2.n[2] = fx[2] - Target[2];18451846if (!_cmsMAT3solve(&tmp, &Jacobian, &tmp2))1847return FALSE;18481849// Move our guess1850x[0] -= (cmsFloat32Number) tmp.n[0];1851x[1] -= (cmsFloat32Number) tmp.n[1];1852x[2] -= (cmsFloat32Number) tmp.n[2];18531854// Some clipping....1855for (j=0; j < 3; j++) {1856if (x[j] < 0) x[j] = 0;1857else1858if (x[j] > 1.0) x[j] = 1.0;1859}1860}18611862return TRUE;1863}18641865186618671868