#include "fast_float_internal.h"
#define SIGMOID_POINTS 1024
typedef struct {
cmsContext ContextID;
const cmsInterpParams* p;
cmsFloat32Number sigmoidIn[SIGMOID_POINTS]; cmsFloat32Number sigmoidOut[SIGMOID_POINTS];
} LabCLUTdata;
typedef struct {
LabCLUTdata* data;
cmsPipeline* original;
} ResamplingContainer;
#define TYPE_SIGMOID 109
cmsINLINE cmsFloat32Number LinLerp1D(cmsFloat32Number Value, const cmsFloat32Number* LutTable)
{
if (Value >= 1.0f)
{
return LutTable[SIGMOID_POINTS - 1];
}
else
if (Value <= 0)
{
return LutTable[0];
}
else
{
cmsFloat32Number y1, y0;
cmsFloat32Number rest;
int cell0, cell1;
Value *= (SIGMOID_POINTS - 1);
cell0 = _cmsQuickFloor(Value);
cell1 = cell0 + 1;
rest = Value - cell0;
y0 = LutTable[cell0];
y1 = LutTable[cell1];
return y0 + (y1 - y0) * rest;
}
}
static
void tabulateSigmoid(cmsContext ContextID, cmsInt32Number type, cmsFloat32Number table[], cmsInt32Number tablePoints)
{
const cmsFloat64Number sigmoidal_slope = 2.5;
cmsToneCurve* original;
cmsInt32Number i;
memset(table, 0, sizeof(cmsFloat32Number) * tablePoints);
original = cmsBuildParametricToneCurve(ContextID, type, &sigmoidal_slope);
if (original != NULL)
{
for (i = 0; i < tablePoints; i++)
{
cmsFloat32Number v = (cmsFloat32Number)i / (cmsFloat32Number)(tablePoints - 1);
table[i] = fclamp(cmsEvalToneCurveFloat(original, v));
}
cmsFreeToneCurve(original);
}
}
static
LabCLUTdata* LabCLUTAlloc(cmsContext ContextID, const cmsInterpParams* p)
{
LabCLUTdata* fd;
fd = (LabCLUTdata*) _cmsMallocZero(ContextID, sizeof(LabCLUTdata));
if (fd == NULL) return NULL;
fd ->ContextID = ContextID;
fd ->p = p;
tabulateSigmoid(ContextID, +TYPE_SIGMOID, fd->sigmoidIn, SIGMOID_POINTS);
tabulateSigmoid(ContextID, -TYPE_SIGMOID, fd->sigmoidOut, SIGMOID_POINTS);
return fd;
}
static
void LabCLUTFree(cmsContext ContextID, void* v)
{
_cmsFree(ContextID, v);
}
static
int XFormSampler(CMSREGISTER const cmsFloat32Number In[], CMSREGISTER cmsFloat32Number Out[], CMSREGISTER void* Cargo)
{
ResamplingContainer* container = (ResamplingContainer*)Cargo;
cmsFloat32Number linearized[3];
linearized[0] = In[0];
linearized[1] = LinLerp1D(In[1], container->data->sigmoidOut);
linearized[2] = LinLerp1D(In[2], container->data->sigmoidOut);
cmsPipelineEvalFloat(linearized, Out, container->original);
return TRUE;
}
cmsINLINE cmsFloat32Number fclamp128(cmsFloat32Number v)
{
return ((v < -128) || isnan(v)) ? -128.0f : (v > 128.0f ? 128.0f : v);
}
cmsINLINE cmsFloat32Number fclamp100(cmsFloat32Number v)
{
return ((v < 1.0e-9f) || isnan(v)) ? 0.0f : (v > 100.0f ? 100.0f : v);
}
#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
static
void LabCLUTEval(struct _cmstransform_struct* CMMcargo,
const void* Input,
void* Output,
cmsUInt32Number PixelsPerLine,
cmsUInt32Number LineCount,
const cmsStride* Stride)
{
LabCLUTdata* pfloat = (LabCLUTdata*)_cmsGetTransformUserData(CMMcargo);
cmsFloat32Number l, a, b;
cmsFloat32Number px, py, pz;
int x0, y0, z0;
int X0, Y0, Z0, X1, Y1, Z1;
cmsFloat32Number rx, ry, rz;
cmsFloat32Number c0, c1 = 0, c2 = 0, c3 = 0;
cmsUInt32Number OutChan;
const cmsInterpParams* p = pfloat->p;
cmsUInt32Number TotalOut = p->nOutputs;
cmsUInt32Number TotalPlusAlpha;
const cmsFloat32Number* LutTable = (const cmsFloat32Number*)p->Table;
cmsUInt32Number i, ii;
const cmsUInt8Number* lin;
const cmsUInt8Number* ain;
const cmsUInt8Number* bin;
const cmsUInt8Number* xin = NULL;
cmsUInt8Number* out[cmsMAXCHANNELS];
cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS];
cmsUInt32Number SourceIncrements[cmsMAXCHANNELS];
cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS];
cmsUInt32Number DestIncrements[cmsMAXCHANNELS];
cmsUInt32Number InputFormat = cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo);
cmsUInt32Number OutputFormat = cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo);
cmsUInt32Number nchans, nalpha;
cmsUInt32Number strideIn, strideOut;
_cmsComputeComponentIncrements(InputFormat, Stride->BytesPerPlaneIn, &nchans, &nalpha, SourceStartingOrder, SourceIncrements);
_cmsComputeComponentIncrements(OutputFormat, Stride->BytesPerPlaneOut, &nchans, &nalpha, DestStartingOrder, DestIncrements);
if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA))
nalpha = 0;
strideIn = strideOut = 0;
for (i = 0; i < LineCount; i++) {
lin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn;
ain = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn;
bin = (const cmsUInt8Number*)Input + SourceStartingOrder[2] + strideIn;
if (nalpha)
xin = (const cmsUInt8Number*)Input + SourceStartingOrder[3] + strideIn;
TotalPlusAlpha = TotalOut;
if (xin) TotalPlusAlpha++;
for (ii = 0; ii < TotalPlusAlpha; ii++)
out[ii] = (cmsUInt8Number*)Output + DestStartingOrder[ii] + strideOut;
for (ii = 0; ii < PixelsPerLine; ii++) {
l = fclamp100( *(cmsFloat32Number*)lin ) / 100.0f;
a = LinLerp1D((( fclamp128( *(cmsFloat32Number*)ain)) + 128.0f) / 255.0f, pfloat->sigmoidIn);
b = LinLerp1D((( fclamp128( *(cmsFloat32Number*)bin)) + 128.0f) / 255.0f, pfloat->sigmoidIn);
lin += SourceIncrements[0];
ain += SourceIncrements[1];
bin += SourceIncrements[2];
px = l * p->Domain[0];
py = a * p->Domain[1];
pz = b * p->Domain[2];
x0 = _cmsQuickFloor(px); rx = (px - (cmsFloat32Number)x0);
y0 = _cmsQuickFloor(py); ry = (py - (cmsFloat32Number)y0);
z0 = _cmsQuickFloor(pz); rz = (pz - (cmsFloat32Number)z0);
X0 = p->opta[2] * x0;
X1 = X0 + (l >= 1.0f ? 0 : p->opta[2]);
Y0 = p->opta[1] * y0;
Y1 = Y0 + (a >= 1.0f ? 0 : p->opta[1]);
Z0 = p->opta[0] * z0;
Z1 = Z0 + (b >= 1.0f ? 0 : p->opta[0]);
for (OutChan = 0; OutChan < TotalOut; OutChan++) {
c0 = DENS(X0, Y0, Z0);
if (rx >= ry && ry >= rz) {
c1 = DENS(X1, Y0, Z0) - c0;
c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0);
c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
}
else
if (rx >= rz && rz >= ry) {
c1 = DENS(X1, Y0, Z0) - c0;
c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0);
}
else
if (rz >= rx && rx >= ry) {
c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1);
c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
c3 = DENS(X0, Y0, Z1) - c0;
}
else
if (ry >= rx && rx >= rz) {
c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0);
c2 = DENS(X0, Y1, Z0) - c0;
c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
}
else
if (ry >= rz && rz >= rx) {
c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
c2 = DENS(X0, Y1, Z0) - c0;
c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0);
}
else
if (rz >= ry && ry >= rx) {
c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1);
c3 = DENS(X0, Y0, Z1) - c0;
}
else {
c1 = c2 = c3 = 0;
}
*(cmsFloat32Number*)(out[OutChan]) = c0 + c1 * rx + c2 * ry + c3 * rz;
out[OutChan] += DestIncrements[OutChan];
}
if (xin)
{
*(cmsFloat32Number*) (out[TotalOut]) = *(cmsFloat32Number*)xin;
xin += SourceIncrements[3];
out[TotalOut] += DestIncrements[TotalOut];
}
}
strideIn += Stride->BytesPerLineIn;
strideOut += Stride->BytesPerLineOut;
}
}
#undef DENS
static
int GetGridpoints(cmsUInt32Number dwFlags)
{
if (dwFlags & 0x00FF0000) {
return (dwFlags >> 16) & 0xFF;
}
if (dwFlags & cmsFLAGS_HIGHRESPRECALC) {
return 66;
}
else
if (dwFlags & cmsFLAGS_LOWRESPRECALC) {
return 33;
}
else
return 51;
}
cmsBool OptimizeCLUTLabTransform(_cmsTransform2Fn* TransformFn,
void** UserData,
_cmsFreeUserDataFn* FreeDataFn,
cmsPipeline** Lut,
cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat,
cmsUInt32Number* dwFlags)
{
cmsPipeline* OriginalLut;
int nGridPoints;
cmsPipeline* OptimizedLUT = NULL;
cmsStage* OptimizedCLUTmpe;
LabCLUTdata* pfloat;
cmsContext ContextID;
_cmsStageCLutData* data;
ResamplingContainer container;
if (*Lut == NULL) return FALSE;
if (!T_FLOAT(*InputFormat) || !T_FLOAT(*OutputFormat)) return FALSE;
if (T_BYTES(*InputFormat) != sizeof(cmsFloat32Number) ||
T_BYTES(*OutputFormat) != sizeof(cmsFloat32Number)) return FALSE;
if (T_COLORSPACE(*InputFormat) != PT_Lab) return FALSE;
OriginalLut = *Lut;
ContextID = cmsGetPipelineContextID(OriginalLut);
nGridPoints = GetGridpoints(*dwFlags);
OptimizedLUT = cmsPipelineAlloc(cmsGetPipelineContextID(OriginalLut), 3, cmsPipelineOutputChannels(OriginalLut));
if (OptimizedLUT == NULL) goto Error;
OptimizedCLUTmpe = cmsStageAllocCLutFloat(ContextID, nGridPoints, 3, cmsPipelineOutputChannels(OriginalLut), NULL);
cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedCLUTmpe);
data = (_cmsStageCLutData*) cmsStageData(OptimizedCLUTmpe);
pfloat = LabCLUTAlloc(ContextID, data ->Params);
if (pfloat == NULL) return FALSE;
container.data = pfloat;
container.original = OriginalLut;
if (!cmsStageSampleCLutFloat(OptimizedCLUTmpe, XFormSampler, (void*)&container, 0)) goto Error;
cmsPipelineFree(OriginalLut);
*Lut = OptimizedLUT;
*TransformFn = LabCLUTEval;
*UserData = pfloat;
*FreeDataFn = LabCLUTFree;
*dwFlags &= ~cmsFLAGS_CAN_CHANGE_FORMATTER;
return TRUE;
Error:
if (OptimizedLUT != NULL) cmsPipelineFree(OptimizedLUT);
return FALSE;
}