Path: blob/master/src/java.desktop/share/native/liblcms/cmsintrp.c
41152 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"5657// This module incorporates several interpolation routines, for 1 to 8 channels on input and58// up to 65535 channels on output. The user may change those by using the interpolation plug-in5960// Some people may want to compile as C++ with all warnings on, in this case make compiler silent61#ifdef _MSC_VER62# if (_MSC_VER >= 1400)63# pragma warning( disable : 4365 )64# endif65#endif6667// Interpolation routines by default68static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags);6970// This is the default factory71_cmsInterpPluginChunkType _cmsInterpPluginChunk = { NULL };7273// The interpolation plug-in memory chunk allocator/dup74void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src)75{76void* from;7778_cmsAssert(ctx != NULL);7980if (src != NULL) {81from = src ->chunks[InterpPlugin];82}83else {84static _cmsInterpPluginChunkType InterpPluginChunk = { NULL };8586from = &InterpPluginChunk;87}8889_cmsAssert(from != NULL);90ctx ->chunks[InterpPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsInterpPluginChunkType));91}929394// Main plug-in entry95cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Data)96{97cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data;98_cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin);99100if (Data == NULL) {101102ptr ->Interpolators = NULL;103return TRUE;104}105106// Set replacement functions107ptr ->Interpolators = Plugin ->InterpolatorsFactory;108return TRUE;109}110111112// Set the interpolation method113cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p)114{115_cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin);116117p ->Interpolation.Lerp16 = NULL;118119// Invoke factory, possibly in the Plug-in120if (ptr ->Interpolators != NULL)121p ->Interpolation = ptr->Interpolators(p -> nInputs, p ->nOutputs, p ->dwFlags);122123// If unsupported by the plug-in, go for the LittleCMS default.124// If happens only if an extern plug-in is being used125if (p ->Interpolation.Lerp16 == NULL)126p ->Interpolation = DefaultInterpolatorsFactory(p ->nInputs, p ->nOutputs, p ->dwFlags);127128// Check for valid interpolator (we just check one member of the union)129if (p ->Interpolation.Lerp16 == NULL) {130return FALSE;131}132133return TRUE;134}135136137// This function precalculates as many parameters as possible to speed up the interpolation.138cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID,139const cmsUInt32Number nSamples[],140cmsUInt32Number InputChan, cmsUInt32Number OutputChan,141const void *Table,142cmsUInt32Number dwFlags)143{144cmsInterpParams* p;145cmsUInt32Number i;146147// Check for maximum inputs148if (InputChan > MAX_INPUT_DIMENSIONS) {149cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", InputChan, MAX_INPUT_DIMENSIONS);150return NULL;151}152153// Creates an empty object154p = (cmsInterpParams*) _cmsMallocZero(ContextID, sizeof(cmsInterpParams));155if (p == NULL) return NULL;156157// Keep original parameters158p -> dwFlags = dwFlags;159p -> nInputs = InputChan;160p -> nOutputs = OutputChan;161p ->Table = Table;162p ->ContextID = ContextID;163164// Fill samples per input direction and domain (which is number of nodes minus one)165for (i=0; i < InputChan; i++) {166167p -> nSamples[i] = nSamples[i];168p -> Domain[i] = nSamples[i] - 1;169}170171// Compute factors to apply to each component to index the grid array172p -> opta[0] = p -> nOutputs;173for (i=1; i < InputChan; i++)174p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i];175176177if (!_cmsSetInterpolationRoutine(ContextID, p)) {178cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported interpolation (%d->%d channels)", InputChan, OutputChan);179_cmsFree(ContextID, p);180return NULL;181}182183// All seems ok184return p;185}186187188// This one is a wrapper on the anterior, but assuming all directions have same number of nodes189cmsInterpParams* CMSEXPORT _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples,190cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags)191{192int i;193cmsUInt32Number Samples[MAX_INPUT_DIMENSIONS];194195// Fill the auxiliary array196for (i=0; i < MAX_INPUT_DIMENSIONS; i++)197Samples[i] = nSamples;198199// Call the extended function200return _cmsComputeInterpParamsEx(ContextID, Samples, InputChan, OutputChan, Table, dwFlags);201}202203204// Free all associated memory205void CMSEXPORT _cmsFreeInterpParams(cmsInterpParams* p)206{207if (p != NULL) _cmsFree(p ->ContextID, p);208}209210211// Inline fixed point interpolation212cmsINLINE CMS_NO_SANITIZE cmsUInt16Number LinearInterp(cmsS15Fixed16Number a, cmsS15Fixed16Number l, cmsS15Fixed16Number h)213{214cmsUInt32Number dif = (cmsUInt32Number) (h - l) * a + 0x8000;215dif = (dif >> 16) + l;216return (cmsUInt16Number) (dif);217}218219220// Linear interpolation (Fixed-point optimized)221static222void LinLerp1D(CMSREGISTER const cmsUInt16Number Value[],223CMSREGISTER cmsUInt16Number Output[],224CMSREGISTER const cmsInterpParams* p)225{226cmsUInt16Number y1, y0;227int cell0, rest;228int val3;229const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table;230231// if last value...232if (Value[0] == 0xffff) {233234Output[0] = LutTable[p -> Domain[0]];235}236else237{238val3 = p->Domain[0] * Value[0];239val3 = _cmsToFixedDomain(val3); // To fixed 15.16240241cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits242rest = FIXED_REST_TO_INT(val3); // Rest is 16 LSB bits243244y0 = LutTable[cell0];245y1 = LutTable[cell0 + 1];246247Output[0] = LinearInterp(rest, y0, y1);248}249}250251// To prevent out of bounds indexing252cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v)253{254return ((v < 1.0e-9f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v);255}256257// Floating-point version of 1D interpolation258static259void LinLerp1Dfloat(const cmsFloat32Number Value[],260cmsFloat32Number Output[],261const cmsInterpParams* p)262{263cmsFloat32Number y1, y0;264cmsFloat32Number val2, rest;265int cell0, cell1;266const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table;267268val2 = fclamp(Value[0]);269270// if last value...271if (val2 == 1.0) {272Output[0] = LutTable[p -> Domain[0]];273}274else275{276val2 *= p->Domain[0];277278cell0 = (int)floor(val2);279cell1 = (int)ceil(val2);280281// Rest is 16 LSB bits282rest = val2 - cell0;283284y0 = LutTable[cell0];285y1 = LutTable[cell1];286287Output[0] = y0 + (y1 - y0) * rest;288}289}290291292293// Eval gray LUT having only one input channel294static CMS_NO_SANITIZE295void Eval1Input(CMSREGISTER const cmsUInt16Number Input[],296CMSREGISTER cmsUInt16Number Output[],297CMSREGISTER const cmsInterpParams* p16)298{299cmsS15Fixed16Number fk;300cmsS15Fixed16Number k0, k1, rk, K0, K1;301int v;302cmsUInt32Number OutChan;303const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;304305v = Input[0] * p16 -> Domain[0];306fk = _cmsToFixedDomain(v);307308k0 = FIXED_TO_INT(fk);309rk = (cmsUInt16Number) FIXED_REST_TO_INT(fk);310311k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0);312313K0 = p16 -> opta[0] * k0;314K1 = p16 -> opta[0] * k1;315316for (OutChan=0; OutChan < p16->nOutputs; OutChan++) {317318Output[OutChan] = LinearInterp(rk, LutTable[K0+OutChan], LutTable[K1+OutChan]);319}320}321322323324// Eval gray LUT having only one input channel325static326void Eval1InputFloat(const cmsFloat32Number Value[],327cmsFloat32Number Output[],328const cmsInterpParams* p)329{330cmsFloat32Number y1, y0;331cmsFloat32Number val2, rest;332int cell0, cell1;333cmsUInt32Number OutChan;334const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table;335336val2 = fclamp(Value[0]);337338// if last value...339if (val2 == 1.0) {340341y0 = LutTable[p->Domain[0]];342343for (OutChan = 0; OutChan < p->nOutputs; OutChan++) {344Output[OutChan] = y0;345}346}347else348{349val2 *= p->Domain[0];350351cell0 = (int)floor(val2);352cell1 = (int)ceil(val2);353354// Rest is 16 LSB bits355rest = val2 - cell0;356357cell0 *= p->opta[0];358cell1 *= p->opta[0];359360for (OutChan = 0; OutChan < p->nOutputs; OutChan++) {361362y0 = LutTable[cell0 + OutChan];363y1 = LutTable[cell1 + OutChan];364365Output[OutChan] = y0 + (y1 - y0) * rest;366}367}368}369370// Bilinear interpolation (16 bits) - cmsFloat32Number version371static372void BilinearInterpFloat(const cmsFloat32Number Input[],373cmsFloat32Number Output[],374const cmsInterpParams* p)375376{377# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a)))378# define DENS(i,j) (LutTable[(i)+(j)+OutChan])379380const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table;381cmsFloat32Number px, py;382int x0, y0,383X0, Y0, X1, Y1;384int TotalOut, OutChan;385cmsFloat32Number fx, fy,386d00, d01, d10, d11,387dx0, dx1,388dxy;389390TotalOut = p -> nOutputs;391px = fclamp(Input[0]) * p->Domain[0];392py = fclamp(Input[1]) * p->Domain[1];393394x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0;395y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0;396397X0 = p -> opta[1] * x0;398X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[1]);399400Y0 = p -> opta[0] * y0;401Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[0]);402403for (OutChan = 0; OutChan < TotalOut; OutChan++) {404405d00 = DENS(X0, Y0);406d01 = DENS(X0, Y1);407d10 = DENS(X1, Y0);408d11 = DENS(X1, Y1);409410dx0 = LERP(fx, d00, d10);411dx1 = LERP(fx, d01, d11);412413dxy = LERP(fy, dx0, dx1);414415Output[OutChan] = dxy;416}417418419# undef LERP420# undef DENS421}422423// Bilinear interpolation (16 bits) - optimized version424static CMS_NO_SANITIZE425void BilinearInterp16(CMSREGISTER const cmsUInt16Number Input[],426CMSREGISTER cmsUInt16Number Output[],427CMSREGISTER const cmsInterpParams* p)428429{430#define DENS(i,j) (LutTable[(i)+(j)+OutChan])431#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a)))432433const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table;434int OutChan, TotalOut;435cmsS15Fixed16Number fx, fy;436CMSREGISTER int rx, ry;437int x0, y0;438CMSREGISTER int X0, X1, Y0, Y1;439440int d00, d01, d10, d11,441dx0, dx1,442dxy;443444TotalOut = p -> nOutputs;445446fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]);447x0 = FIXED_TO_INT(fx);448rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain449450451fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]);452y0 = FIXED_TO_INT(fy);453ry = FIXED_REST_TO_INT(fy);454455456X0 = p -> opta[1] * x0;457X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[1]);458459Y0 = p -> opta[0] * y0;460Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[0]);461462for (OutChan = 0; OutChan < TotalOut; OutChan++) {463464d00 = DENS(X0, Y0);465d01 = DENS(X0, Y1);466d10 = DENS(X1, Y0);467d11 = DENS(X1, Y1);468469dx0 = LERP(rx, d00, d10);470dx1 = LERP(rx, d01, d11);471472dxy = LERP(ry, dx0, dx1);473474Output[OutChan] = (cmsUInt16Number) dxy;475}476477478# undef LERP479# undef DENS480}481482483// Trilinear interpolation (16 bits) - cmsFloat32Number version484static485void TrilinearInterpFloat(const cmsFloat32Number Input[],486cmsFloat32Number Output[],487const cmsInterpParams* p)488489{490# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a)))491# define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])492493const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table;494cmsFloat32Number px, py, pz;495int x0, y0, z0,496X0, Y0, Z0, X1, Y1, Z1;497int TotalOut, OutChan;498499cmsFloat32Number fx, fy, fz,500d000, d001, d010, d011,501d100, d101, d110, d111,502dx00, dx01, dx10, dx11,503dxy0, dxy1, dxyz;504505TotalOut = p -> nOutputs;506507// We need some clipping here508px = fclamp(Input[0]) * p->Domain[0];509py = fclamp(Input[1]) * p->Domain[1];510pz = fclamp(Input[2]) * p->Domain[2];511512x0 = (int) floor(px); fx = px - (cmsFloat32Number) x0; // We need full floor funcionality here513y0 = (int) floor(py); fy = py - (cmsFloat32Number) y0;514z0 = (int) floor(pz); fz = pz - (cmsFloat32Number) z0;515516X0 = p -> opta[2] * x0;517X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]);518519Y0 = p -> opta[1] * y0;520Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]);521522Z0 = p -> opta[0] * z0;523Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]);524525for (OutChan = 0; OutChan < TotalOut; OutChan++) {526527d000 = DENS(X0, Y0, Z0);528d001 = DENS(X0, Y0, Z1);529d010 = DENS(X0, Y1, Z0);530d011 = DENS(X0, Y1, Z1);531532d100 = DENS(X1, Y0, Z0);533d101 = DENS(X1, Y0, Z1);534d110 = DENS(X1, Y1, Z0);535d111 = DENS(X1, Y1, Z1);536537538dx00 = LERP(fx, d000, d100);539dx01 = LERP(fx, d001, d101);540dx10 = LERP(fx, d010, d110);541dx11 = LERP(fx, d011, d111);542543dxy0 = LERP(fy, dx00, dx10);544dxy1 = LERP(fy, dx01, dx11);545546dxyz = LERP(fz, dxy0, dxy1);547548Output[OutChan] = dxyz;549}550551552# undef LERP553# undef DENS554}555556// Trilinear interpolation (16 bits) - optimized version557static CMS_NO_SANITIZE558void TrilinearInterp16(CMSREGISTER const cmsUInt16Number Input[],559CMSREGISTER cmsUInt16Number Output[],560CMSREGISTER const cmsInterpParams* p)561562{563#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])564#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a)))565566const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table;567int OutChan, TotalOut;568cmsS15Fixed16Number fx, fy, fz;569CMSREGISTER int rx, ry, rz;570int x0, y0, z0;571CMSREGISTER int X0, X1, Y0, Y1, Z0, Z1;572int d000, d001, d010, d011,573d100, d101, d110, d111,574dx00, dx01, dx10, dx11,575dxy0, dxy1, dxyz;576577TotalOut = p -> nOutputs;578579fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]);580x0 = FIXED_TO_INT(fx);581rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain582583584fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]);585y0 = FIXED_TO_INT(fy);586ry = FIXED_REST_TO_INT(fy);587588fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]);589z0 = FIXED_TO_INT(fz);590rz = FIXED_REST_TO_INT(fz);591592593X0 = p -> opta[2] * x0;594X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[2]);595596Y0 = p -> opta[1] * y0;597Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[1]);598599Z0 = p -> opta[0] * z0;600Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta[0]);601602for (OutChan = 0; OutChan < TotalOut; OutChan++) {603604d000 = DENS(X0, Y0, Z0);605d001 = DENS(X0, Y0, Z1);606d010 = DENS(X0, Y1, Z0);607d011 = DENS(X0, Y1, Z1);608609d100 = DENS(X1, Y0, Z0);610d101 = DENS(X1, Y0, Z1);611d110 = DENS(X1, Y1, Z0);612d111 = DENS(X1, Y1, Z1);613614615dx00 = LERP(rx, d000, d100);616dx01 = LERP(rx, d001, d101);617dx10 = LERP(rx, d010, d110);618dx11 = LERP(rx, d011, d111);619620dxy0 = LERP(ry, dx00, dx10);621dxy1 = LERP(ry, dx01, dx11);622623dxyz = LERP(rz, dxy0, dxy1);624625Output[OutChan] = (cmsUInt16Number) dxyz;626}627628629# undef LERP630# undef DENS631}632633634// Tetrahedral interpolation, using Sakamoto algorithm.635#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])636static637void TetrahedralInterpFloat(const cmsFloat32Number Input[],638cmsFloat32Number Output[],639const cmsInterpParams* p)640{641const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;642cmsFloat32Number px, py, pz;643int x0, y0, z0,644X0, Y0, Z0, X1, Y1, Z1;645cmsFloat32Number rx, ry, rz;646cmsFloat32Number c0, c1=0, c2=0, c3=0;647int OutChan, TotalOut;648649TotalOut = p -> nOutputs;650651// We need some clipping here652px = fclamp(Input[0]) * p->Domain[0];653py = fclamp(Input[1]) * p->Domain[1];654pz = fclamp(Input[2]) * p->Domain[2];655656x0 = (int) floor(px); rx = (px - (cmsFloat32Number) x0); // We need full floor functionality here657y0 = (int) floor(py); ry = (py - (cmsFloat32Number) y0);658z0 = (int) floor(pz); rz = (pz - (cmsFloat32Number) z0);659660661X0 = p -> opta[2] * x0;662X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]);663664Y0 = p -> opta[1] * y0;665Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]);666667Z0 = p -> opta[0] * z0;668Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]);669670for (OutChan=0; OutChan < TotalOut; OutChan++) {671672// These are the 6 Tetrahedral673674c0 = DENS(X0, Y0, Z0);675676if (rx >= ry && ry >= rz) {677678c1 = DENS(X1, Y0, Z0) - c0;679c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0);680c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);681682}683else684if (rx >= rz && rz >= ry) {685686c1 = DENS(X1, Y0, Z0) - c0;687c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);688c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0);689690}691else692if (rz >= rx && rx >= ry) {693694c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1);695c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);696c3 = DENS(X0, Y0, Z1) - c0;697698}699else700if (ry >= rx && rx >= rz) {701702c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0);703c2 = DENS(X0, Y1, Z0) - c0;704c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);705706}707else708if (ry >= rz && rz >= rx) {709710c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);711c2 = DENS(X0, Y1, Z0) - c0;712c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0);713714}715else716if (rz >= ry && ry >= rx) {717718c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);719c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1);720c3 = DENS(X0, Y0, Z1) - c0;721722}723else {724c1 = c2 = c3 = 0;725}726727Output[OutChan] = c0 + c1 * rx + c2 * ry + c3 * rz;728}729730}731732#undef DENS733734static CMS_NO_SANITIZE735void TetrahedralInterp16(CMSREGISTER const cmsUInt16Number Input[],736CMSREGISTER cmsUInt16Number Output[],737CMSREGISTER const cmsInterpParams* p)738{739const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table;740cmsS15Fixed16Number fx, fy, fz;741cmsS15Fixed16Number rx, ry, rz;742int x0, y0, z0;743cmsS15Fixed16Number c0, c1, c2, c3, Rest;744cmsUInt32Number X0, X1, Y0, Y1, Z0, Z1;745cmsUInt32Number TotalOut = p -> nOutputs;746747fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]);748fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]);749fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]);750751x0 = FIXED_TO_INT(fx);752y0 = FIXED_TO_INT(fy);753z0 = FIXED_TO_INT(fz);754755rx = FIXED_REST_TO_INT(fx);756ry = FIXED_REST_TO_INT(fy);757rz = FIXED_REST_TO_INT(fz);758759X0 = p -> opta[2] * x0;760X1 = (Input[0] == 0xFFFFU ? 0 : p->opta[2]);761762Y0 = p -> opta[1] * y0;763Y1 = (Input[1] == 0xFFFFU ? 0 : p->opta[1]);764765Z0 = p -> opta[0] * z0;766Z1 = (Input[2] == 0xFFFFU ? 0 : p->opta[0]);767768LutTable += X0+Y0+Z0;769770// Output should be computed as x = ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))771// which expands as: x = (Rest + ((Rest+0x7fff)/0xFFFF) + 0x8000)>>16772// This can be replaced by: t = Rest+0x8001, x = (t + (t>>16))>>16773// at the cost of being off by one at 7fff and 17ffe.774775if (rx >= ry) {776if (ry >= rz) {777Y1 += X1;778Z1 += Y1;779for (; TotalOut; TotalOut--) {780c1 = LutTable[X1];781c2 = LutTable[Y1];782c3 = LutTable[Z1];783c0 = *LutTable++;784c3 -= c2;785c2 -= c1;786c1 -= c0;787Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;788*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);789}790} else if (rz >= rx) {791X1 += Z1;792Y1 += X1;793for (; TotalOut; TotalOut--) {794c1 = LutTable[X1];795c2 = LutTable[Y1];796c3 = LutTable[Z1];797c0 = *LutTable++;798c2 -= c1;799c1 -= c3;800c3 -= c0;801Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;802*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);803}804} else {805Z1 += X1;806Y1 += Z1;807for (; TotalOut; TotalOut--) {808c1 = LutTable[X1];809c2 = LutTable[Y1];810c3 = LutTable[Z1];811c0 = *LutTable++;812c2 -= c3;813c3 -= c1;814c1 -= c0;815Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;816*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);817}818}819} else {820if (rx >= rz) {821X1 += Y1;822Z1 += X1;823for (; TotalOut; TotalOut--) {824c1 = LutTable[X1];825c2 = LutTable[Y1];826c3 = LutTable[Z1];827c0 = *LutTable++;828c3 -= c1;829c1 -= c2;830c2 -= c0;831Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;832*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);833}834} else if (ry >= rz) {835Z1 += Y1;836X1 += Z1;837for (; TotalOut; TotalOut--) {838c1 = LutTable[X1];839c2 = LutTable[Y1];840c3 = LutTable[Z1];841c0 = *LutTable++;842c1 -= c3;843c3 -= c2;844c2 -= c0;845Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;846*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);847}848} else {849Y1 += Z1;850X1 += Y1;851for (; TotalOut; TotalOut--) {852c1 = LutTable[X1];853c2 = LutTable[Y1];854c3 = LutTable[Z1];855c0 = *LutTable++;856c1 -= c2;857c2 -= c3;858c3 -= c0;859Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;860*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);861}862}863}864}865866867#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])868static CMS_NO_SANITIZE869void Eval4Inputs(CMSREGISTER const cmsUInt16Number Input[],870CMSREGISTER cmsUInt16Number Output[],871CMSREGISTER const cmsInterpParams* p16)872{873const cmsUInt16Number* LutTable;874cmsS15Fixed16Number fk;875cmsS15Fixed16Number k0, rk;876int K0, K1;877cmsS15Fixed16Number fx, fy, fz;878cmsS15Fixed16Number rx, ry, rz;879int x0, y0, z0;880cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;881cmsUInt32Number i;882cmsS15Fixed16Number c0, c1, c2, c3, Rest;883cmsUInt32Number OutChan;884cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];885886887fk = _cmsToFixedDomain((int) Input[0] * p16 -> Domain[0]);888fx = _cmsToFixedDomain((int) Input[1] * p16 -> Domain[1]);889fy = _cmsToFixedDomain((int) Input[2] * p16 -> Domain[2]);890fz = _cmsToFixedDomain((int) Input[3] * p16 -> Domain[3]);891892k0 = FIXED_TO_INT(fk);893x0 = FIXED_TO_INT(fx);894y0 = FIXED_TO_INT(fy);895z0 = FIXED_TO_INT(fz);896897rk = FIXED_REST_TO_INT(fk);898rx = FIXED_REST_TO_INT(fx);899ry = FIXED_REST_TO_INT(fy);900rz = FIXED_REST_TO_INT(fz);901902K0 = p16 -> opta[3] * k0;903K1 = K0 + (Input[0] == 0xFFFFU ? 0 : p16->opta[3]);904905X0 = p16 -> opta[2] * x0;906X1 = X0 + (Input[1] == 0xFFFFU ? 0 : p16->opta[2]);907908Y0 = p16 -> opta[1] * y0;909Y1 = Y0 + (Input[2] == 0xFFFFU ? 0 : p16->opta[1]);910911Z0 = p16 -> opta[0] * z0;912Z1 = Z0 + (Input[3] == 0xFFFFU ? 0 : p16->opta[0]);913914LutTable = (cmsUInt16Number*) p16 -> Table;915LutTable += K0;916917for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) {918919c0 = DENS(X0, Y0, Z0);920921if (rx >= ry && ry >= rz) {922923c1 = DENS(X1, Y0, Z0) - c0;924c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0);925c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);926927}928else929if (rx >= rz && rz >= ry) {930931c1 = DENS(X1, Y0, Z0) - c0;932c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);933c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0);934935}936else937if (rz >= rx && rx >= ry) {938939c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1);940c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);941c3 = DENS(X0, Y0, Z1) - c0;942943}944else945if (ry >= rx && rx >= rz) {946947c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0);948c2 = DENS(X0, Y1, Z0) - c0;949c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);950951}952else953if (ry >= rz && rz >= rx) {954955c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);956c2 = DENS(X0, Y1, Z0) - c0;957c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0);958959}960else961if (rz >= ry && ry >= rx) {962963c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);964c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1);965c3 = DENS(X0, Y0, Z1) - c0;966967}968else {969c1 = c2 = c3 = 0;970}971972Rest = c1 * rx + c2 * ry + c3 * rz;973974Tmp1[OutChan] = (cmsUInt16Number)(c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));975}976977978LutTable = (cmsUInt16Number*) p16 -> Table;979LutTable += K1;980981for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) {982983c0 = DENS(X0, Y0, Z0);984985if (rx >= ry && ry >= rz) {986987c1 = DENS(X1, Y0, Z0) - c0;988c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0);989c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);990991}992else993if (rx >= rz && rz >= ry) {994995c1 = DENS(X1, Y0, Z0) - c0;996c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);997c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0);998999}1000else1001if (rz >= rx && rx >= ry) {10021003c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1);1004c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);1005c3 = DENS(X0, Y0, Z1) - c0;10061007}1008else1009if (ry >= rx && rx >= rz) {10101011c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0);1012c2 = DENS(X0, Y1, Z0) - c0;1013c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);10141015}1016else1017if (ry >= rz && rz >= rx) {10181019c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);1020c2 = DENS(X0, Y1, Z0) - c0;1021c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0);10221023}1024else1025if (rz >= ry && ry >= rx) {10261027c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);1028c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1);1029c3 = DENS(X0, Y0, Z1) - c0;10301031}1032else {1033c1 = c2 = c3 = 0;1034}10351036Rest = c1 * rx + c2 * ry + c3 * rz;10371038Tmp2[OutChan] = (cmsUInt16Number) (c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));1039}1040104110421043for (i=0; i < p16 -> nOutputs; i++) {1044Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]);1045}1046}1047#undef DENS104810491050// For more that 3 inputs (i.e., CMYK)1051// evaluate two 3-dimensional interpolations and then linearly interpolate between them.1052static1053void Eval4InputsFloat(const cmsFloat32Number Input[],1054cmsFloat32Number Output[],1055const cmsInterpParams* p)1056{1057const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;1058cmsFloat32Number rest;1059cmsFloat32Number pk;1060int k0, K0, K1;1061const cmsFloat32Number* T;1062cmsUInt32Number i;1063cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];1064cmsInterpParams p1;10651066pk = fclamp(Input[0]) * p->Domain[0];1067k0 = _cmsQuickFloor(pk);1068rest = pk - (cmsFloat32Number) k0;10691070K0 = p -> opta[3] * k0;1071K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[3]);10721073p1 = *p;1074memmove(&p1.Domain[0], &p ->Domain[1], 3*sizeof(cmsUInt32Number));10751076T = LutTable + K0;1077p1.Table = T;10781079TetrahedralInterpFloat(Input + 1, Tmp1, &p1);10801081T = LutTable + K1;1082p1.Table = T;1083TetrahedralInterpFloat(Input + 1, Tmp2, &p1);10841085for (i=0; i < p -> nOutputs; i++)1086{1087cmsFloat32Number y0 = Tmp1[i];1088cmsFloat32Number y1 = Tmp2[i];10891090Output[i] = y0 + (y1 - y0) * rest;1091}1092}10931094#define EVAL_FNS(N,NM) static CMS_NO_SANITIZE \1095void Eval##N##Inputs(CMSREGISTER const cmsUInt16Number Input[], CMSREGISTER cmsUInt16Number Output[], CMSREGISTER const cmsInterpParams* p16)\1096{\1097const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;\1098cmsS15Fixed16Number fk;\1099cmsS15Fixed16Number k0, rk;\1100int K0, K1;\1101const cmsUInt16Number* T;\1102cmsUInt32Number i;\1103cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];\1104cmsInterpParams p1;\1105\1106fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]);\1107k0 = FIXED_TO_INT(fk);\1108rk = FIXED_REST_TO_INT(fk);\1109\1110K0 = p16 -> opta[NM] * k0;\1111K1 = p16 -> opta[NM] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));\1112\1113p1 = *p16;\1114memmove(&p1.Domain[0], &p16 ->Domain[1], NM*sizeof(cmsUInt32Number));\1115\1116T = LutTable + K0;\1117p1.Table = T;\1118\1119Eval##NM##Inputs(Input + 1, Tmp1, &p1);\1120\1121T = LutTable + K1;\1122p1.Table = T;\1123\1124Eval##NM##Inputs(Input + 1, Tmp2, &p1);\1125\1126for (i=0; i < p16 -> nOutputs; i++) {\1127\1128Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]);\1129}\1130}\1131\1132static void Eval##N##InputsFloat(const cmsFloat32Number Input[], \1133cmsFloat32Number Output[],\1134const cmsInterpParams * p)\1135{\1136const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;\1137cmsFloat32Number rest;\1138cmsFloat32Number pk;\1139int k0, K0, K1;\1140const cmsFloat32Number* T;\1141cmsUInt32Number i;\1142cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];\1143cmsInterpParams p1;\1144\1145pk = fclamp(Input[0]) * p->Domain[0];\1146k0 = _cmsQuickFloor(pk);\1147rest = pk - (cmsFloat32Number) k0;\1148\1149K0 = p -> opta[NM] * k0;\1150K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[NM]);\1151\1152p1 = *p;\1153memmove(&p1.Domain[0], &p ->Domain[1], NM*sizeof(cmsUInt32Number));\1154\1155T = LutTable + K0;\1156p1.Table = T;\1157\1158Eval##NM##InputsFloat(Input + 1, Tmp1, &p1);\1159\1160T = LutTable + K1;\1161p1.Table = T;\1162\1163Eval##NM##InputsFloat(Input + 1, Tmp2, &p1);\1164\1165for (i=0; i < p -> nOutputs; i++) {\1166\1167cmsFloat32Number y0 = Tmp1[i];\1168cmsFloat32Number y1 = Tmp2[i];\1169\1170Output[i] = y0 + (y1 - y0) * rest;\1171}\1172}117311741175/**1176* Thanks to Carles Llopis for the templating idea1177*/1178EVAL_FNS(5, 4)1179EVAL_FNS(6, 5)1180EVAL_FNS(7, 6)1181EVAL_FNS(8, 7)1182EVAL_FNS(9, 8)1183EVAL_FNS(10, 9)1184EVAL_FNS(11, 10)1185EVAL_FNS(12, 11)1186EVAL_FNS(13, 12)1187EVAL_FNS(14, 13)1188EVAL_FNS(15, 14)118911901191// The default factory1192static1193cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags)1194{11951196cmsInterpFunction Interpolation;1197cmsBool IsFloat = (dwFlags & CMS_LERP_FLAGS_FLOAT);1198cmsBool IsTrilinear = (dwFlags & CMS_LERP_FLAGS_TRILINEAR);11991200memset(&Interpolation, 0, sizeof(Interpolation));12011202// Safety check1203if (nInputChannels >= 4 && nOutputChannels >= MAX_STAGE_CHANNELS)1204return Interpolation;12051206switch (nInputChannels) {12071208case 1: // Gray LUT / linear12091210if (nOutputChannels == 1) {12111212if (IsFloat)1213Interpolation.LerpFloat = LinLerp1Dfloat;1214else1215Interpolation.Lerp16 = LinLerp1D;12161217}1218else {12191220if (IsFloat)1221Interpolation.LerpFloat = Eval1InputFloat;1222else1223Interpolation.Lerp16 = Eval1Input;1224}1225break;12261227case 2: // Duotone1228if (IsFloat)1229Interpolation.LerpFloat = BilinearInterpFloat;1230else1231Interpolation.Lerp16 = BilinearInterp16;1232break;12331234case 3: // RGB et al12351236if (IsTrilinear) {12371238if (IsFloat)1239Interpolation.LerpFloat = TrilinearInterpFloat;1240else1241Interpolation.Lerp16 = TrilinearInterp16;1242}1243else {12441245if (IsFloat)1246Interpolation.LerpFloat = TetrahedralInterpFloat;1247else {12481249Interpolation.Lerp16 = TetrahedralInterp16;1250}1251}1252break;12531254case 4: // CMYK lut12551256if (IsFloat)1257Interpolation.LerpFloat = Eval4InputsFloat;1258else1259Interpolation.Lerp16 = Eval4Inputs;1260break;12611262case 5: // 5 Inks1263if (IsFloat)1264Interpolation.LerpFloat = Eval5InputsFloat;1265else1266Interpolation.Lerp16 = Eval5Inputs;1267break;12681269case 6: // 6 Inks1270if (IsFloat)1271Interpolation.LerpFloat = Eval6InputsFloat;1272else1273Interpolation.Lerp16 = Eval6Inputs;1274break;12751276case 7: // 7 inks1277if (IsFloat)1278Interpolation.LerpFloat = Eval7InputsFloat;1279else1280Interpolation.Lerp16 = Eval7Inputs;1281break;12821283case 8: // 8 inks1284if (IsFloat)1285Interpolation.LerpFloat = Eval8InputsFloat;1286else1287Interpolation.Lerp16 = Eval8Inputs;1288break;12891290case 9:1291if (IsFloat)1292Interpolation.LerpFloat = Eval9InputsFloat;1293else1294Interpolation.Lerp16 = Eval9Inputs;1295break;12961297case 10:1298if (IsFloat)1299Interpolation.LerpFloat = Eval10InputsFloat;1300else1301Interpolation.Lerp16 = Eval10Inputs;1302break;13031304case 11:1305if (IsFloat)1306Interpolation.LerpFloat = Eval11InputsFloat;1307else1308Interpolation.Lerp16 = Eval11Inputs;1309break;13101311case 12:1312if (IsFloat)1313Interpolation.LerpFloat = Eval12InputsFloat;1314else1315Interpolation.Lerp16 = Eval12Inputs;1316break;13171318case 13:1319if (IsFloat)1320Interpolation.LerpFloat = Eval13InputsFloat;1321else1322Interpolation.Lerp16 = Eval13Inputs;1323break;13241325case 14:1326if (IsFloat)1327Interpolation.LerpFloat = Eval14InputsFloat;1328else1329Interpolation.Lerp16 = Eval14Inputs;1330break;13311332case 15:1333if (IsFloat)1334Interpolation.LerpFloat = Eval15InputsFloat;1335else1336Interpolation.Lerp16 = Eval15Inputs;1337break;13381339default:1340Interpolation.Lerp16 = NULL;1341}13421343return Interpolation;1344}134513461347