Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/native/sun/java2d/cmm/lcms/cmsgamma.c
38918 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//54#include "lcms2_internal.h"5556// Tone curves are powerful constructs that can contain curves specified in diverse ways.57// The curve is stored in segments, where each segment can be sampled or specified by parameters.58// a 16.bit simplification of the *whole* curve is kept for optimization purposes. For float operation,59// each segment is evaluated separately. Plug-ins may be used to define new parametric schemes,60// each plug-in may define up to MAX_TYPES_IN_LCMS_PLUGIN functions types. For defining a function,61// the plug-in should provide the type id, how many parameters each type has, and a pointer to62// a procedure that evaluates the function. In the case of reverse evaluation, the evaluator will63// be called with the type id as a negative value, and a sampled version of the reversed curve64// will be built.6566// ----------------------------------------------------------------- Implementation67// Maxim number of nodes68#define MAX_NODES_IN_CURVE 409769#define MINUS_INF (-1E22F)70#define PLUS_INF (+1E22F)7172// The list of supported parametric curves73typedef struct _cmsParametricCurvesCollection_st {7475cmsUInt32Number nFunctions; // Number of supported functions in this chunk76cmsInt32Number FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types77cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function7879cmsParametricCurveEvaluator Evaluator; // The evaluator8081struct _cmsParametricCurvesCollection_st* Next; // Next in list8283} _cmsParametricCurvesCollection;8485// This is the default (built-in) evaluator86static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R);8788// The built-in list89static _cmsParametricCurvesCollection DefaultCurves = {909, // # of curve types91{ 1, 2, 3, 4, 5, 6, 7, 8, 108 }, // Parametric curve ID92{ 1, 3, 4, 5, 7, 4, 5, 5, 1 }, // Parameters by type93DefaultEvalParametricFn, // Evaluator94NULL // Next in chain95};9697// Duplicates the zone of memory used by the plug-in in the new context98static99void DupPluginCurvesList(struct _cmsContext_struct* ctx,100const struct _cmsContext_struct* src)101{102_cmsCurvesPluginChunkType newHead = { NULL };103_cmsParametricCurvesCollection* entry;104_cmsParametricCurvesCollection* Anterior = NULL;105_cmsCurvesPluginChunkType* head = (_cmsCurvesPluginChunkType*) src->chunks[CurvesPlugin];106107_cmsAssert(head != NULL);108109// Walk the list copying all nodes110for (entry = head->ParametricCurves;111entry != NULL;112entry = entry ->Next) {113114_cmsParametricCurvesCollection *newEntry = ( _cmsParametricCurvesCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsParametricCurvesCollection));115116if (newEntry == NULL)117return;118119// We want to keep the linked list order, so this is a little bit tricky120newEntry -> Next = NULL;121if (Anterior)122Anterior -> Next = newEntry;123124Anterior = newEntry;125126if (newHead.ParametricCurves == NULL)127newHead.ParametricCurves = newEntry;128}129130ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsCurvesPluginChunkType));131}132133// The allocator have to follow the chain134void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx,135const struct _cmsContext_struct* src)136{137_cmsAssert(ctx != NULL);138139if (src != NULL) {140141// Copy all linked list142DupPluginCurvesList(ctx, src);143}144else {145static _cmsCurvesPluginChunkType CurvesPluginChunk = { NULL };146ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx ->MemPool, &CurvesPluginChunk, sizeof(_cmsCurvesPluginChunkType));147}148}149150151// The linked list head152_cmsCurvesPluginChunkType _cmsCurvesPluginChunk = { NULL };153154// As a way to install new parametric curves155cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Data)156{157_cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin);158cmsPluginParametricCurves* Plugin = (cmsPluginParametricCurves*) Data;159_cmsParametricCurvesCollection* fl;160161if (Data == NULL) {162163ctx -> ParametricCurves = NULL;164return TRUE;165}166167fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsParametricCurvesCollection));168if (fl == NULL) return FALSE;169170// Copy the parameters171fl ->Evaluator = Plugin ->Evaluator;172fl ->nFunctions = Plugin ->nFunctions;173174// Make sure no mem overwrites175if (fl ->nFunctions > MAX_TYPES_IN_LCMS_PLUGIN)176fl ->nFunctions = MAX_TYPES_IN_LCMS_PLUGIN;177178// Copy the data179memmove(fl->FunctionTypes, Plugin ->FunctionTypes, fl->nFunctions * sizeof(cmsUInt32Number));180memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number));181182// Keep linked list183fl ->Next = ctx->ParametricCurves;184ctx->ParametricCurves = fl;185186// All is ok187return TRUE;188}189190191// Search in type list, return position or -1 if not found192static193int IsInSet(int Type, _cmsParametricCurvesCollection* c)194{195int i;196197for (i=0; i < (int) c ->nFunctions; i++)198if (abs(Type) == c ->FunctionTypes[i]) return i;199200return -1;201}202203204// Search for the collection which contains a specific type205static206_cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index)207{208_cmsParametricCurvesCollection* c;209int Position;210_cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin);211212for (c = ctx->ParametricCurves; c != NULL; c = c ->Next) {213214Position = IsInSet(Type, c);215216if (Position != -1) {217if (index != NULL)218*index = Position;219return c;220}221}222// If none found, revert for defaults223for (c = &DefaultCurves; c != NULL; c = c ->Next) {224225Position = IsInSet(Type, c);226227if (Position != -1) {228if (index != NULL)229*index = Position;230return c;231}232}233234return NULL;235}236237// Low level allocate, which takes care of memory details. nEntries may be zero, and in this case238// no optimation curve is computed. nSegments may also be zero in the inverse case, where only the239// optimization curve is given. Both features simultaneously is an error240static241cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEntries,242cmsUInt32Number nSegments, const cmsCurveSegment* Segments,243const cmsUInt16Number* Values)244{245cmsToneCurve* p;246cmsUInt32Number i;247248// We allow huge tables, which are then restricted for smoothing operations249if (nEntries > 65530) {250cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve of more than 65530 entries");251return NULL;252}253254if (nEntries == 0 && nSegments == 0) {255cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve with zero segments and no table");256return NULL;257}258259// Allocate all required pointers, etc.260p = (cmsToneCurve*) _cmsMallocZero(ContextID, sizeof(cmsToneCurve));261if (!p) return NULL;262263// In this case, there are no segments264if (nSegments == 0) {265p ->Segments = NULL;266p ->Evals = NULL;267}268else {269p ->Segments = (cmsCurveSegment*) _cmsCalloc(ContextID, nSegments, sizeof(cmsCurveSegment));270if (p ->Segments == NULL) goto Error;271272p ->Evals = (cmsParametricCurveEvaluator*) _cmsCalloc(ContextID, nSegments, sizeof(cmsParametricCurveEvaluator));273if (p ->Evals == NULL) goto Error;274}275276p -> nSegments = nSegments;277278// This 16-bit table contains a limited precision representation of the whole curve and is kept for279// increasing xput on certain operations.280if (nEntries == 0) {281p ->Table16 = NULL;282}283else {284p ->Table16 = (cmsUInt16Number*) _cmsCalloc(ContextID, nEntries, sizeof(cmsUInt16Number));285if (p ->Table16 == NULL) goto Error;286}287288p -> nEntries = nEntries;289290// Initialize members if requested291if (Values != NULL && (nEntries > 0)) {292293for (i=0; i < nEntries; i++)294p ->Table16[i] = Values[i];295}296297// Initialize the segments stuff. The evaluator for each segment is located and a pointer to it298// is placed in advance to maximize performance.299if (Segments != NULL && (nSegments > 0)) {300301_cmsParametricCurvesCollection *c;302303p ->SegInterp = (cmsInterpParams**) _cmsCalloc(ContextID, nSegments, sizeof(cmsInterpParams*));304if (p ->SegInterp == NULL) goto Error;305306for (i=0; i < nSegments; i++) {307308// Type 0 is a special marker for table-based curves309if (Segments[i].Type == 0)310p ->SegInterp[i] = _cmsComputeInterpParams(ContextID, Segments[i].nGridPoints, 1, 1, NULL, CMS_LERP_FLAGS_FLOAT);311312memmove(&p ->Segments[i], &Segments[i], sizeof(cmsCurveSegment));313314if (Segments[i].Type == 0 && Segments[i].SampledPoints != NULL)315p ->Segments[i].SampledPoints = (cmsFloat32Number*) _cmsDupMem(ContextID, Segments[i].SampledPoints, sizeof(cmsFloat32Number) * Segments[i].nGridPoints);316else317p ->Segments[i].SampledPoints = NULL;318319320c = GetParametricCurveByType(ContextID, Segments[i].Type, NULL);321if (c != NULL)322p ->Evals[i] = c ->Evaluator;323}324}325326p ->InterpParams = _cmsComputeInterpParams(ContextID, p ->nEntries, 1, 1, p->Table16, CMS_LERP_FLAGS_16BITS);327if (p->InterpParams != NULL)328return p;329330Error:331if (p -> SegInterp) _cmsFree(ContextID, p -> SegInterp);332if (p -> Segments) _cmsFree(ContextID, p -> Segments);333if (p -> Evals) _cmsFree(ContextID, p -> Evals);334if (p ->Table16) _cmsFree(ContextID, p ->Table16);335_cmsFree(ContextID, p);336return NULL;337}338339340// Parametric Fn using floating point341static342cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R)343{344cmsFloat64Number e, Val, disc;345346switch (Type) {347348// X = Y ^ Gamma349case 1:350if (R < 0) {351352if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE)353Val = R;354else355Val = 0;356}357else358Val = pow(R, Params[0]);359break;360361// Type 1 Reversed: X = Y ^1/gamma362case -1:363if (R < 0) {364365if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE)366Val = R;367else368Val = 0;369}370else371{372if (fabs(Params[0]) < MATRIX_DET_TOLERANCE)373Val = PLUS_INF;374else375Val = pow(R, 1 / Params[0]);376}377break;378379// CIE 122-1966380// Y = (aX + b)^Gamma | X >= -b/a381// Y = 0 | else382case 2:383{384385if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)386{387Val = 0;388}389else390{391disc = -Params[2] / Params[1];392393if (R >= disc) {394395e = Params[1] * R + Params[2];396397if (e > 0)398Val = pow(e, Params[0]);399else400Val = 0;401}402else403Val = 0;404}405}406break;407408// Type 2 Reversed409// X = (Y ^1/g - b) / a410case -2:411{412if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||413fabs(Params[1]) < MATRIX_DET_TOLERANCE)414{415Val = 0;416}417else418{419if (R < 0)420Val = 0;421else422Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1];423424if (Val < 0)425Val = 0;426}427}428break;429430431// IEC 61966-3432// Y = (aX + b)^Gamma | X <= -b/a433// Y = c | else434case 3:435{436if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)437{438Val = 0;439}440else441{442disc = -Params[2] / Params[1];443if (disc < 0)444disc = 0;445446if (R >= disc) {447448e = Params[1] * R + Params[2];449450if (e > 0)451Val = pow(e, Params[0]) + Params[3];452else453Val = 0;454}455else456Val = Params[3];457}458}459break;460461462// Type 3 reversed463// X=((Y-c)^1/g - b)/a | (Y>=c)464// X=-b/a | (Y<c)465case -3:466{467if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)468{469Val = 0;470}471else472{473if (R >= Params[3]) {474475e = R - Params[3];476477if (e > 0)478Val = (pow(e, 1 / Params[0]) - Params[2]) / Params[1];479else480Val = 0;481}482else {483Val = -Params[2] / Params[1];484}485}486}487break;488489490// IEC 61966-2.1 (sRGB)491// Y = (aX + b)^Gamma | X >= d492// Y = cX | X < d493case 4:494if (R >= Params[4]) {495496e = Params[1]*R + Params[2];497498if (e > 0)499Val = pow(e, Params[0]);500else501Val = 0;502}503else504Val = R * Params[3];505break;506507// Type 4 reversed508// X=((Y^1/g-b)/a) | Y >= (ad+b)^g509// X=Y/c | Y< (ad+b)^g510case -4:511{512if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||513fabs(Params[1]) < MATRIX_DET_TOLERANCE ||514fabs(Params[3]) < MATRIX_DET_TOLERANCE)515{516Val = 0;517}518else519{520e = Params[1] * Params[4] + Params[2];521if (e < 0)522disc = 0;523else524disc = pow(e, Params[0]);525526if (R >= disc) {527528Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1];529}530else {531Val = R / Params[3];532}533}534}535break;536537538// Y = (aX + b)^Gamma + e | X >= d539// Y = cX + f | X < d540case 5:541if (R >= Params[4]) {542543e = Params[1]*R + Params[2];544545if (e > 0)546Val = pow(e, Params[0]) + Params[5];547else548Val = Params[5];549}550else551Val = R*Params[3] + Params[6];552break;553554555// Reversed type 5556// X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e), cd+f557// X=(Y-f)/c | else558case -5:559{560if (fabs(Params[1]) < MATRIX_DET_TOLERANCE ||561fabs(Params[3]) < MATRIX_DET_TOLERANCE)562{563Val = 0;564}565else566{567disc = Params[3] * Params[4] + Params[6];568if (R >= disc) {569570e = R - Params[5];571if (e < 0)572Val = 0;573else574Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1];575}576else {577Val = (R - Params[6]) / Params[3];578}579}580}581break;582583584// Types 6,7,8 comes from segmented curves as described in ICCSpecRevision_02_11_06_Float.pdf585// Type 6 is basically identical to type 5 without d586587// Y = (a * X + b) ^ Gamma + c588case 6:589e = Params[1]*R + Params[2];590591if (e < 0)592Val = Params[3];593else594Val = pow(e, Params[0]) + Params[3];595break;596597// ((Y - c) ^1/Gamma - b) / a598case -6:599{600if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)601{602Val = 0;603}604else605{606e = R - Params[3];607if (e < 0)608Val = 0;609else610Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1];611}612}613break;614615616// Y = a * log (b * X^Gamma + c) + d617case 7:618619e = Params[2] * pow(R, Params[0]) + Params[3];620if (e <= 0)621Val = Params[4];622else623Val = Params[1]*log10(e) + Params[4];624break;625626// (Y - d) / a = log(b * X ^Gamma + c)627// pow(10, (Y-d) / a) = b * X ^Gamma + c628// pow((pow(10, (Y-d) / a) - c) / b, 1/g) = X629case -7:630{631if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||632fabs(Params[1]) < MATRIX_DET_TOLERANCE ||633fabs(Params[2]) < MATRIX_DET_TOLERANCE)634{635Val = 0;636}637else638{639Val = pow((pow(10.0, (R - Params[4]) / Params[1]) - Params[3]) / Params[2], 1.0 / Params[0]);640}641}642break;643644645//Y = a * b^(c*X+d) + e646case 8:647Val = (Params[0] * pow(Params[1], Params[2] * R + Params[3]) + Params[4]);648break;649650651// Y = (log((y-e) / a) / log(b) - d ) / c652// a=0, b=1, c=2, d=3, e=4,653case -8:654655disc = R - Params[4];656if (disc < 0) Val = 0;657else658{659if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||660fabs(Params[2]) < MATRIX_DET_TOLERANCE)661{662Val = 0;663}664else665{666Val = (log(disc / Params[0]) / log(Params[1]) - Params[3]) / Params[2];667}668}669break;670671// S-Shaped: (1 - (1-x)^1/g)^1/g672case 108:673if (fabs(Params[0]) < MATRIX_DET_TOLERANCE)674Val = 0;675else676Val = pow(1.0 - pow(1 - R, 1/Params[0]), 1/Params[0]);677break;678679// y = (1 - (1-x)^1/g)^1/g680// y^g = (1 - (1-x)^1/g)681// 1 - y^g = (1-x)^1/g682// (1 - y^g)^g = 1 - x683// 1 - (1 - y^g)^g684case -108:685Val = 1 - pow(1 - pow(R, Params[0]), Params[0]);686break;687688default:689// Unsupported parametric curve. Should never reach here690return 0;691}692693return Val;694}695696// Evaluate a segmented function for a single value. Return -Inf if no valid segment found .697// If fn type is 0, perform an interpolation on the table698static699cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R)700{701int i;702cmsFloat32Number Out32;703cmsFloat64Number Out;704705for (i = (int) g->nSegments - 1; i >= 0; --i) {706707// Check for domain708if ((R > g->Segments[i].x0) && (R <= g->Segments[i].x1)) {709710// Type == 0 means segment is sampled711if (g->Segments[i].Type == 0) {712713cmsFloat32Number R1 = (cmsFloat32Number)(R - g->Segments[i].x0) / (g->Segments[i].x1 - g->Segments[i].x0);714715// Setup the table (TODO: clean that)716g->SegInterp[i]->Table = g->Segments[i].SampledPoints;717718g->SegInterp[i]->Interpolation.LerpFloat(&R1, &Out32, g->SegInterp[i]);719Out = (cmsFloat64Number) Out32;720721}722else {723Out = g->Evals[i](g->Segments[i].Type, g->Segments[i].Params, R);724}725726if (isinf(Out))727return PLUS_INF;728else729{730if (isinf(-Out))731return MINUS_INF;732}733734return Out;735}736}737738return MINUS_INF;739}740741// Access to estimated low-res table742cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t)743{744_cmsAssert(t != NULL);745return t ->nEntries;746}747748const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t)749{750_cmsAssert(t != NULL);751return t ->Table16;752}753754755// Create an empty gamma curve, by using tables. This specifies only the limited-precision part, and leaves the756// floating point description empty.757cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsUInt32Number nEntries, const cmsUInt16Number Values[])758{759return AllocateToneCurveStruct(ContextID, nEntries, 0, NULL, Values);760}761762static763cmsUInt32Number EntriesByGamma(cmsFloat64Number Gamma)764{765if (fabs(Gamma - 1.0) < 0.001) return 2;766return 4096;767}768769770// Create a segmented gamma, fill the table771cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID,772cmsUInt32Number nSegments, const cmsCurveSegment Segments[])773{774cmsUInt32Number i;775cmsFloat64Number R, Val;776cmsToneCurve* g;777cmsUInt32Number nGridPoints = 4096;778779_cmsAssert(Segments != NULL);780781// Optimizatin for identity curves.782if (nSegments == 1 && Segments[0].Type == 1) {783784nGridPoints = EntriesByGamma(Segments[0].Params[0]);785}786787g = AllocateToneCurveStruct(ContextID, nGridPoints, nSegments, Segments, NULL);788if (g == NULL) return NULL;789790// Once we have the floating point version, we can approximate a 16 bit table of 4096 entries791// for performance reasons. This table would normally not be used except on 8/16 bits transforms.792for (i = 0; i < nGridPoints; i++) {793794R = (cmsFloat64Number) i / (nGridPoints-1);795796Val = EvalSegmentedFn(g, R);797798// Round and saturate799g ->Table16[i] = _cmsQuickSaturateWord(Val * 65535.0);800}801802return g;803}804805// Use a segmented curve to store the floating point table806cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[])807{808cmsCurveSegment Seg[3];809810// A segmented tone curve should have function segments in the first and last positions811// Initialize segmented curve part up to 0 to constant value = samples[0]812Seg[0].x0 = MINUS_INF;813Seg[0].x1 = 0;814Seg[0].Type = 6;815816Seg[0].Params[0] = 1;817Seg[0].Params[1] = 0;818Seg[0].Params[2] = 0;819Seg[0].Params[3] = values[0];820Seg[0].Params[4] = 0;821822// From zero to 1823Seg[1].x0 = 0;824Seg[1].x1 = 1.0;825Seg[1].Type = 0;826827Seg[1].nGridPoints = nEntries;828Seg[1].SampledPoints = (cmsFloat32Number*) values;829830// Final segment is constant = lastsample831Seg[2].x0 = 1.0;832Seg[2].x1 = PLUS_INF;833Seg[2].Type = 6;834835Seg[2].Params[0] = 1;836Seg[2].Params[1] = 0;837Seg[2].Params[2] = 0;838Seg[2].Params[3] = values[nEntries-1];839Seg[2].Params[4] = 0;840841842return cmsBuildSegmentedToneCurve(ContextID, 3, Seg);843}844845// Parametric curves846//847// Parameters goes as: Curve, a, b, c, d, e, f848// Type is the ICC type +1849// if type is negative, then the curve is analytically inverted850cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[])851{852cmsCurveSegment Seg0;853int Pos = 0;854cmsUInt32Number size;855_cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos);856857_cmsAssert(Params != NULL);858859if (c == NULL) {860cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type);861return NULL;862}863864memset(&Seg0, 0, sizeof(Seg0));865866Seg0.x0 = MINUS_INF;867Seg0.x1 = PLUS_INF;868Seg0.Type = Type;869870size = c->ParameterCount[Pos] * sizeof(cmsFloat64Number);871memmove(Seg0.Params, Params, size);872873return cmsBuildSegmentedToneCurve(ContextID, 1, &Seg0);874}875876877878// Build a gamma table based on gamma constant879cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma)880{881return cmsBuildParametricToneCurve(ContextID, 1, &Gamma);882}883884885// Free all memory taken by the gamma curve886void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve)887{888cmsContext ContextID;889890if (Curve == NULL) return;891892ContextID = Curve ->InterpParams->ContextID;893894_cmsFreeInterpParams(Curve ->InterpParams);895896if (Curve -> Table16)897_cmsFree(ContextID, Curve ->Table16);898899if (Curve ->Segments) {900901cmsUInt32Number i;902903for (i=0; i < Curve ->nSegments; i++) {904905if (Curve ->Segments[i].SampledPoints) {906_cmsFree(ContextID, Curve ->Segments[i].SampledPoints);907}908909if (Curve ->SegInterp[i] != 0)910_cmsFreeInterpParams(Curve->SegInterp[i]);911}912913_cmsFree(ContextID, Curve ->Segments);914_cmsFree(ContextID, Curve ->SegInterp);915}916917if (Curve -> Evals)918_cmsFree(ContextID, Curve -> Evals);919920_cmsFree(ContextID, Curve);921}922923// Utility function, free 3 gamma tables924void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3])925{926927_cmsAssert(Curve != NULL);928929if (Curve[0] != NULL) cmsFreeToneCurve(Curve[0]);930if (Curve[1] != NULL) cmsFreeToneCurve(Curve[1]);931if (Curve[2] != NULL) cmsFreeToneCurve(Curve[2]);932933Curve[0] = Curve[1] = Curve[2] = NULL;934}935936937// Duplicate a gamma table938cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* In)939{940if (In == NULL) return NULL;941942return AllocateToneCurveStruct(In ->InterpParams ->ContextID, In ->nEntries, In ->nSegments, In ->Segments, In ->Table16);943}944945// Joins two curves for X and Y. Curves should be monotonic.946// We want to get947//948// y = Y^-1(X(t))949//950cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID,951const cmsToneCurve* X,952const cmsToneCurve* Y, cmsUInt32Number nResultingPoints)953{954cmsToneCurve* out = NULL;955cmsToneCurve* Yreversed = NULL;956cmsFloat32Number t, x;957cmsFloat32Number* Res = NULL;958cmsUInt32Number i;959960961_cmsAssert(X != NULL);962_cmsAssert(Y != NULL);963964Yreversed = cmsReverseToneCurveEx(nResultingPoints, Y);965if (Yreversed == NULL) goto Error;966967Res = (cmsFloat32Number*) _cmsCalloc(ContextID, nResultingPoints, sizeof(cmsFloat32Number));968if (Res == NULL) goto Error;969970//Iterate971for (i=0; i < nResultingPoints; i++) {972973t = (cmsFloat32Number) i / (nResultingPoints-1);974x = cmsEvalToneCurveFloat(X, t);975Res[i] = cmsEvalToneCurveFloat(Yreversed, x);976}977978// Allocate space for output979out = cmsBuildTabulatedToneCurveFloat(ContextID, nResultingPoints, Res);980981Error:982983if (Res != NULL) _cmsFree(ContextID, Res);984if (Yreversed != NULL) cmsFreeToneCurve(Yreversed);985986return out;987}988989990991// Get the surrounding nodes. This is tricky on non-monotonic tables992static993int GetInterval(cmsFloat64Number In, const cmsUInt16Number LutTable[], const struct _cms_interp_struc* p)994{995int i;996int y0, y1;997998// A 1 point table is not allowed999if (p -> Domain[0] < 1) return -1;10001001// Let's see if ascending or descending.1002if (LutTable[0] < LutTable[p ->Domain[0]]) {10031004// Table is overall ascending1005for (i = (int) p->Domain[0] - 1; i >= 0; --i) {10061007y0 = LutTable[i];1008y1 = LutTable[i+1];10091010if (y0 <= y1) { // Increasing1011if (In >= y0 && In <= y1) return i;1012}1013else1014if (y1 < y0) { // Decreasing1015if (In >= y1 && In <= y0) return i;1016}1017}1018}1019else {1020// Table is overall descending1021for (i=0; i < (int) p -> Domain[0]; i++) {10221023y0 = LutTable[i];1024y1 = LutTable[i+1];10251026if (y0 <= y1) { // Increasing1027if (In >= y0 && In <= y1) return i;1028}1029else1030if (y1 < y0) { // Decreasing1031if (In >= y1 && In <= y0) return i;1032}1033}1034}10351036return -1;1037}10381039// Reverse a gamma table1040cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsUInt32Number nResultSamples, const cmsToneCurve* InCurve)1041{1042cmsToneCurve *out;1043cmsFloat64Number a = 0, b = 0, y, x1, y1, x2, y2;1044int i, j;1045int Ascending;10461047_cmsAssert(InCurve != NULL);10481049// Try to reverse it analytically whatever possible10501051if (InCurve ->nSegments == 1 && InCurve ->Segments[0].Type > 0 &&1052/* InCurve -> Segments[0].Type <= 5 */1053GetParametricCurveByType(InCurve ->InterpParams->ContextID, InCurve ->Segments[0].Type, NULL) != NULL) {10541055return cmsBuildParametricToneCurve(InCurve ->InterpParams->ContextID,1056-(InCurve -> Segments[0].Type),1057InCurve -> Segments[0].Params);1058}10591060// Nope, reverse the table.1061out = cmsBuildTabulatedToneCurve16(InCurve ->InterpParams->ContextID, nResultSamples, NULL);1062if (out == NULL)1063return NULL;10641065// We want to know if this is an ascending or descending table1066Ascending = !cmsIsToneCurveDescending(InCurve);10671068// Iterate across Y axis1069for (i=0; i < (int) nResultSamples; i++) {10701071y = (cmsFloat64Number) i * 65535.0 / (nResultSamples - 1);10721073// Find interval in which y is within.1074j = GetInterval(y, InCurve->Table16, InCurve->InterpParams);1075if (j >= 0) {107610771078// Get limits of interval1079x1 = InCurve ->Table16[j];1080x2 = InCurve ->Table16[j+1];10811082y1 = (cmsFloat64Number) (j * 65535.0) / (InCurve ->nEntries - 1);1083y2 = (cmsFloat64Number) ((j+1) * 65535.0 ) / (InCurve ->nEntries - 1);10841085// If collapsed, then use any1086if (x1 == x2) {10871088out ->Table16[i] = _cmsQuickSaturateWord(Ascending ? y2 : y1);1089continue;10901091} else {10921093// Interpolate1094a = (y2 - y1) / (x2 - x1);1095b = y2 - a * x2;1096}1097}10981099out ->Table16[i] = _cmsQuickSaturateWord(a* y + b);1100}110111021103return out;1104}11051106// Reverse a gamma table1107cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma)1108{1109_cmsAssert(InGamma != NULL);11101111return cmsReverseToneCurveEx(4096, InGamma);1112}11131114// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite1115// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press.1116//1117// Smoothing and interpolation with second differences.1118//1119// Input: weights (w), data (y): vector from 1 to m.1120// Input: smoothing parameter (lambda), length (m).1121// Output: smoothed vector (z): vector from 1 to m.11221123static1124cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[],1125cmsFloat32Number z[], cmsFloat32Number lambda, int m)1126{1127int i, i1, i2;1128cmsFloat32Number *c, *d, *e;1129cmsBool st;113011311132c = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number));1133d = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number));1134e = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number));11351136if (c != NULL && d != NULL && e != NULL) {113711381139d[1] = w[1] + lambda;1140c[1] = -2 * lambda / d[1];1141e[1] = lambda /d[1];1142z[1] = w[1] * y[1];1143d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1];1144c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2];1145e[2] = lambda / d[2];1146z[2] = w[2] * y[2] - c[1] * z[1];11471148for (i = 3; i < m - 1; i++) {1149i1 = i - 1; i2 = i - 2;1150d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];1151c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i];1152e[i] = lambda / d[i];1153z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2];1154}11551156i1 = m - 2; i2 = m - 3;11571158d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];1159c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1];1160z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2];1161i1 = m - 1; i2 = m - 2;11621163d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];1164z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m];1165z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m];11661167for (i = m - 2; 1<= i; i--)1168z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2];11691170st = TRUE;1171}1172else st = FALSE;11731174if (c != NULL) _cmsFree(ContextID, c);1175if (d != NULL) _cmsFree(ContextID, d);1176if (e != NULL) _cmsFree(ContextID, e);11771178return st;1179}11801181// Smooths a curve sampled at regular intervals.1182cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda)1183{1184cmsBool SuccessStatus = TRUE;1185cmsFloat32Number *w, *y, *z;1186cmsUInt32Number i, nItems, Zeros, Poles;11871188if (Tab != NULL && Tab->InterpParams != NULL)1189{1190cmsContext ContextID = Tab->InterpParams->ContextID;11911192if (!cmsIsToneCurveLinear(Tab)) // Only non-linear curves need smoothing1193{1194nItems = Tab->nEntries;1195if (nItems < MAX_NODES_IN_CURVE)1196{1197// Allocate one more item than needed1198w = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number));1199y = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number));1200z = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number));12011202if (w != NULL && y != NULL && z != NULL) // Ensure no memory allocation failure1203{1204memset(w, 0, (nItems + 1) * sizeof(cmsFloat32Number));1205memset(y, 0, (nItems + 1) * sizeof(cmsFloat32Number));1206memset(z, 0, (nItems + 1) * sizeof(cmsFloat32Number));12071208for (i = 0; i < nItems; i++)1209{1210y[i + 1] = (cmsFloat32Number)Tab->Table16[i];1211w[i + 1] = 1.0;1212}12131214if (smooth2(ContextID, w, y, z, (cmsFloat32Number)lambda, (int)nItems))1215{1216// Do some reality - checking...12171218Zeros = Poles = 0;1219for (i = nItems; i > 1; --i)1220{1221if (z[i] == 0.) Zeros++;1222if (z[i] >= 65535.) Poles++;1223if (z[i] < z[i - 1])1224{1225cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic.");1226SuccessStatus = FALSE;1227break;1228}1229}12301231if (SuccessStatus && Zeros > (nItems / 3))1232{1233cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros.");1234SuccessStatus = FALSE;1235}12361237if (SuccessStatus && Poles > (nItems / 3))1238{1239cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles.");1240SuccessStatus = FALSE;1241}12421243if (SuccessStatus) // Seems ok1244{1245for (i = 0; i < nItems; i++)1246{1247// Clamp to cmsUInt16Number1248Tab->Table16[i] = _cmsQuickSaturateWord(z[i + 1]);1249}1250}1251}1252else // Could not smooth1253{1254cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Function smooth2 failed.");1255SuccessStatus = FALSE;1256}1257}1258else // One or more buffers could not be allocated1259{1260cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Could not allocate memory.");1261SuccessStatus = FALSE;1262}12631264if (z != NULL)1265_cmsFree(ContextID, z);12661267if (y != NULL)1268_cmsFree(ContextID, y);12691270if (w != NULL)1271_cmsFree(ContextID, w);1272}1273else // too many items in the table1274{1275cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Too many points.");1276SuccessStatus = FALSE;1277}1278}1279}1280else // Tab parameter or Tab->InterpParams is NULL1281{1282// Can't signal an error here since the ContextID is not known at this point1283SuccessStatus = FALSE;1284}12851286return SuccessStatus;1287}12881289// Is a table linear? Do not use parametric since we cannot guarantee some weird parameters resulting1290// in a linear table. This way assures it is linear in 12 bits, which should be enough in most cases.1291cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve)1292{1293int i;1294int diff;12951296_cmsAssert(Curve != NULL);12971298for (i=0; i < (int) Curve ->nEntries; i++) {12991300diff = abs((int) Curve->Table16[i] - (int) _cmsQuantizeVal(i, Curve ->nEntries));1301if (diff > 0x0f)1302return FALSE;1303}13041305return TRUE;1306}13071308// Same, but for monotonicity1309cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t)1310{1311cmsUInt32Number n;1312int i, last;1313cmsBool lDescending;13141315_cmsAssert(t != NULL);13161317// Degenerated curves are monotonic? Ok, let's pass them1318n = t ->nEntries;1319if (n < 2) return TRUE;13201321// Curve direction1322lDescending = cmsIsToneCurveDescending(t);13231324if (lDescending) {13251326last = t ->Table16[0];13271328for (i = 1; i < (int) n; i++) {13291330if (t ->Table16[i] - last > 2) // We allow some ripple1331return FALSE;1332else1333last = t ->Table16[i];13341335}1336}1337else {13381339last = t ->Table16[n-1];13401341for (i = (int) n - 2; i >= 0; --i) {13421343if (t ->Table16[i] - last > 2)1344return FALSE;1345else1346last = t ->Table16[i];13471348}1349}13501351return TRUE;1352}13531354// Same, but for descending tables1355cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t)1356{1357_cmsAssert(t != NULL);13581359return t ->Table16[0] > t ->Table16[t ->nEntries-1];1360}136113621363// Another info fn: is out gamma table multisegment?1364cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* t)1365{1366_cmsAssert(t != NULL);13671368return t -> nSegments > 1;1369}13701371cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t)1372{1373_cmsAssert(t != NULL);13741375if (t -> nSegments != 1) return 0;1376return t ->Segments[0].Type;1377}13781379// We need accuracy this time1380cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v)1381{1382_cmsAssert(Curve != NULL);13831384// Check for 16 bits table. If so, this is a limited-precision tone curve1385if (Curve ->nSegments == 0) {13861387cmsUInt16Number In, Out;13881389In = (cmsUInt16Number) _cmsQuickSaturateWord(v * 65535.0);1390Out = cmsEvalToneCurve16(Curve, In);13911392return (cmsFloat32Number) (Out / 65535.0);1393}13941395return (cmsFloat32Number) EvalSegmentedFn(Curve, v);1396}13971398// We need xput over here1399cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v)1400{1401cmsUInt16Number out;14021403_cmsAssert(Curve != NULL);14041405Curve ->InterpParams ->Interpolation.Lerp16(&v, &out, Curve ->InterpParams);1406return out;1407}140814091410// Least squares fitting.1411// A mathematical procedure for finding the best-fitting curve to a given set of points by1412// minimizing the sum of the squares of the offsets ("the residuals") of the points from the curve.1413// The sum of the squares of the offsets is used instead of the offset absolute values because1414// this allows the residuals to be treated as a continuous differentiable quantity.1415//1416// y = f(x) = x ^ g1417//1418// R = (yi - (xi^g))1419// R2 = (yi - (xi^g))21420// SUM R2 = SUM (yi - (xi^g))21421//1422// dR2/dg = -2 SUM x^g log(x)(y - x^g)1423// solving for dR2/dg = 01424//1425// g = 1/n * SUM(log(y) / log(x))14261427cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision)1428{1429cmsFloat64Number gamma, sum, sum2;1430cmsFloat64Number n, x, y, Std;1431cmsUInt32Number i;14321433_cmsAssert(t != NULL);14341435sum = sum2 = n = 0;14361437// Excluding endpoints1438for (i=1; i < (MAX_NODES_IN_CURVE-1); i++) {14391440x = (cmsFloat64Number) i / (MAX_NODES_IN_CURVE-1);1441y = (cmsFloat64Number) cmsEvalToneCurveFloat(t, (cmsFloat32Number) x);14421443// Avoid 7% on lower part to prevent1444// artifacts due to linear ramps14451446if (y > 0. && y < 1. && x > 0.07) {14471448gamma = log(y) / log(x);1449sum += gamma;1450sum2 += gamma * gamma;1451n++;1452}1453}14541455// Take a look on SD to see if gamma isn't exponential at all1456Std = sqrt((n * sum2 - sum * sum) / (n*(n-1)));14571458if (Std > Precision)1459return -1.0;14601461return (sum / n); // The mean1462}146314641465// Retrieve parameters on one-segment tone curves14661467cmsFloat64Number* CMSEXPORT cmsGetToneCurveParams(const cmsToneCurve* t)1468{1469_cmsAssert(t != NULL);14701471if (t->nSegments != 1) return NULL;1472return t->Segments[0].Params;1473}147414751476