#include "fast_float_internal.h"
#define PRELINEARIZATION_POINTS 4096
typedef struct {
cmsContext ContextID;
const cmsInterpParams* p;
cmsUInt16Number rx[256], ry[256], rz[256];
cmsUInt32Number X0[256], Y0[256], Z0[256];
} Performance8Data;
static
Performance8Data* Performance8alloc(cmsContext ContextID, const cmsInterpParams* p, cmsToneCurve* G[3])
{
int i;
cmsUInt16Number Input[3];
cmsS15Fixed16Number v1, v2, v3;
Performance8Data* p8;
p8 = (Performance8Data*) _cmsMallocZero(ContextID, sizeof(Performance8Data));
if (p8 == NULL) return NULL;
for (i=0; i < 256; i++) {
if (G != NULL) {
Input[0] = cmsEvalToneCurve16(G[0], FROM_8_TO_16(i));
Input[1] = cmsEvalToneCurve16(G[1], FROM_8_TO_16(i));
Input[2] = cmsEvalToneCurve16(G[2], FROM_8_TO_16(i));
}
else {
Input[0] = FROM_8_TO_16(i);
Input[1] = FROM_8_TO_16(i);
Input[2] = FROM_8_TO_16(i);
}
v1 = _cmsToFixedDomain(Input[0] * p -> Domain[0]);
v2 = _cmsToFixedDomain(Input[1] * p -> Domain[1]);
v3 = _cmsToFixedDomain(Input[2] * p -> Domain[2]);
p8 ->X0[i] = (p->opta[2] * FIXED_TO_INT(v1));
p8 ->Y0[i] = (p->opta[1] * FIXED_TO_INT(v2));
p8 ->Z0[i] = (p->opta[0] * FIXED_TO_INT(v3));
p8 ->rx[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v1);
p8 ->ry[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v2);
p8 ->rz[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v3);
}
p8 ->ContextID = ContextID;
p8 ->p = p;
return p8;
}
static
void Performance8free(cmsContext ContextID, void* ptr)
{
_cmsFree(ContextID, ptr);
}
static
int XFormSampler16(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
{
cmsPipelineEval16(In, Out, (cmsPipeline*) Cargo);
return TRUE;
}
#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
static
void PerformanceEval8(struct _cmstransform_struct *CMMcargo,
const void* Input,
void* Output,
cmsUInt32Number PixelsPerLine,
cmsUInt32Number LineCount,
const cmsStride* Stride)
{
cmsUInt8Number r, g, b;
cmsS15Fixed16Number rx, ry, rz;
cmsS15Fixed16Number c0, c1, c2, c3, Rest;
cmsUInt32Number OutChan, TotalPlusAlpha;
cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
Performance8Data* p8 = (Performance8Data*)_cmsGetTransformUserData(CMMcargo);
const cmsInterpParams* p = p8->p;
cmsUInt32Number TotalOut = p->nOutputs;
const cmsUInt16Number* LutTable = (const cmsUInt16Number*)p->Table;
cmsUInt8Number* out[cmsMAXCHANNELS];
cmsUInt16Number res16;
cmsUInt32Number i, ii;
cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS];
cmsUInt32Number SourceIncrements[cmsMAXCHANNELS];
cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS];
cmsUInt32Number DestIncrements[cmsMAXCHANNELS];
const cmsUInt8Number* rin;
const cmsUInt8Number* gin;
const cmsUInt8Number* bin;
const cmsUInt8Number* ain = NULL;
cmsUInt32Number nalpha, strideIn, strideOut;
_cmsComputeComponentIncrements(cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneIn, NULL, &nalpha, SourceStartingOrder, SourceIncrements);
_cmsComputeComponentIncrements(cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneOut, NULL, &nalpha, DestStartingOrder, DestIncrements);
if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA))
nalpha = 0;
strideIn = strideOut = 0;
for (i = 0; i < LineCount; i++) {
rin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn;
gin = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn;
bin = (const cmsUInt8Number*)Input + SourceStartingOrder[2] + strideIn;
if (nalpha)
ain = (const cmsUInt8Number*)Input + SourceStartingOrder[3] + strideIn;
TotalPlusAlpha = TotalOut;
if (ain) TotalPlusAlpha++;
for (OutChan = 0; OutChan < TotalPlusAlpha; OutChan++) {
out[OutChan] = (cmsUInt8Number*)Output + DestStartingOrder[OutChan] + strideOut;
}
for (ii = 0; ii < PixelsPerLine; ii++) {
r = *rin; g = *gin; b = *bin;
rin += SourceIncrements[0];
gin += SourceIncrements[1];
bin += SourceIncrements[2];
X0 = X1 = p8->X0[r];
Y0 = Y1 = p8->Y0[g];
Z0 = Z1 = p8->Z0[b];
rx = p8->rx[r];
ry = p8->ry[g];
rz = p8->rz[b];
X1 = X0 + ((rx == 0) ? 0 : p->opta[2]);
Y1 = Y0 + ((ry == 0) ? 0 : p->opta[1]);
Z1 = Z0 + ((rz == 0) ? 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;
}
Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
res16 = (cmsUInt16Number)c0 + ((Rest + (Rest >> 16)) >> 16);
*out[OutChan] = FROM_16_TO_8(res16);
out[OutChan] += DestIncrements[OutChan];
}
if (ain) {
*out[TotalOut] = *ain;
ain += SourceIncrements[3];
out[TotalOut] += DestIncrements[TotalOut];
}
}
strideIn += Stride->BytesPerLineIn;
strideOut += Stride->BytesPerLineOut;
}
}
#undef DENS
static
cmsBool IsDegenerated(const cmsToneCurve* g)
{
int i, Zeros = 0, Poles = 0;
int nEntries = cmsGetToneCurveEstimatedTableEntries(g);
const cmsUInt16Number* Table16 = cmsGetToneCurveEstimatedTable(g);
for (i=0; i < nEntries; i++) {
if (Table16[i] == 0x0000) Zeros++;
if (Table16[i] == 0xffff) Poles++;
}
if (Zeros == 1 && Poles == 1) return FALSE; if (Zeros > (nEntries / 4)) return TRUE; if (Poles > (nEntries / 4)) return TRUE;
return FALSE;
}
static
void SlopeLimiting(cmsUInt16Number* Table16, int nEntries)
{
int BeginVal, EndVal;
int AtBegin = (int) floor((cmsFloat64Number)nEntries * 0.02 + 0.5); int AtEnd = nEntries - AtBegin - 1; cmsFloat64Number Val, Slope, beta;
int i;
if (Table16[0] > Table16[nEntries-1]) {
BeginVal = 0xffff; EndVal = 0;
}
else {
BeginVal = 0; EndVal = 0xffff;
}
Val = Table16[AtBegin];
Slope = (Val - BeginVal) / AtBegin;
beta = Val - Slope * AtBegin;
for (i=0; i < AtBegin; i++)
Table16[i] = _cmsSaturateWord(i * Slope + beta);
Val = Table16[AtEnd];
Slope = (EndVal - Val) / AtBegin; beta = Val - Slope * AtEnd;
for (i = AtEnd; i < (int) nEntries; i++)
Table16[i] = _cmsSaturateWord(i * Slope + beta);
}
cmsBool Optimize8BitRGBTransform(_cmsTransform2Fn* TransformFn,
void** UserData,
_cmsFreeUserDataFn* FreeDataFn,
cmsPipeline** Lut,
cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat,
cmsUInt32Number* dwFlags)
{
cmsPipeline* OriginalLut;
int nGridPoints;
cmsToneCurve *Trans[cmsMAXCHANNELS], *TransReverse[cmsMAXCHANNELS];
cmsUInt32Number t, i, j;
cmsFloat32Number v, In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS];
cmsBool lIsSuitable;
cmsPipeline* OptimizedLUT = NULL, *LutPlusCurves = NULL;
cmsStage* OptimizedCLUTmpe;
cmsStage* OptimizedPrelinMpe;
Performance8Data* p8;
cmsUInt16Number* MyTable[3];
cmsContext ContextID;
_cmsStageCLutData* data;
if (*Lut == NULL) return FALSE;
if (T_FLOAT(*InputFormat) || T_FLOAT(*OutputFormat)) return FALSE;
if (T_BYTES(*InputFormat) != 1 || T_BYTES(*OutputFormat) != 1) return FALSE;
if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE;
if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE;
OriginalLut = *Lut;
ContextID = cmsGetPipelineContextID(OriginalLut);
nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigRgbData, *dwFlags);
memset(Trans, 0, sizeof(Trans));
memset(TransReverse, 0, sizeof(TransReverse));
MyTable[0] = (cmsUInt16Number*) _cmsMallocZero(ContextID, sizeof(cmsUInt16Number) * PRELINEARIZATION_POINTS);
MyTable[1] = (cmsUInt16Number*) _cmsMallocZero(ContextID, sizeof(cmsUInt16Number) * PRELINEARIZATION_POINTS);
MyTable[2] = (cmsUInt16Number*) _cmsMallocZero(ContextID, sizeof(cmsUInt16Number) * PRELINEARIZATION_POINTS);
if (MyTable[0] == NULL || MyTable[1] == NULL || MyTable[2] == NULL) goto Error;
for (i=0; i < PRELINEARIZATION_POINTS; i++) {
v = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1));
for (j=0; j < 3; j++)
In[j] = v;
cmsPipelineEvalFloat(In, Out, OriginalLut);
for (j=0; j < 3; j++)
MyTable[j][i] = _cmsSaturateWord(Out[j] * 65535.0);
}
for (t=0; t < 3; t++) {
SlopeLimiting(MyTable[t], PRELINEARIZATION_POINTS);
Trans[t] = cmsBuildTabulatedToneCurve16(ContextID, PRELINEARIZATION_POINTS, MyTable[t]);
if (Trans[t] == NULL) goto Error;
_cmsFree(cmsGetPipelineContextID(OriginalLut), MyTable[t]);
}
lIsSuitable = TRUE;
for (t=0; (lIsSuitable && (t < 3)); t++) {
if (IsDegenerated(Trans[t]))
lIsSuitable = FALSE;
}
if (!lIsSuitable) goto Error;
for (t = 0; t < cmsPipelineInputChannels(OriginalLut); t++) {
TransReverse[t] = cmsReverseToneCurveEx(PRELINEARIZATION_POINTS, Trans[t]);
if (TransReverse[t] == NULL) goto Error;
}
LutPlusCurves = cmsPipelineDup(OriginalLut);
if (LutPlusCurves == NULL) goto Error;
cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, 3, TransReverse));
OptimizedLUT = cmsPipelineAlloc(cmsGetPipelineContextID(OriginalLut), 3, cmsPipelineOutputChannels(OriginalLut));
if (OptimizedLUT == NULL) goto Error;
OptimizedPrelinMpe = cmsStageAllocToneCurves(ContextID, 3, Trans);
cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe);
OptimizedCLUTmpe = cmsStageAllocCLut16bit(ContextID, nGridPoints, 3, cmsPipelineOutputChannels(OriginalLut), NULL);
cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe);
if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error;
data = (_cmsStageCLutData*) cmsStageData(OptimizedCLUTmpe);
p8 = Performance8alloc(ContextID, data ->Params, Trans);
if (p8 == NULL) return FALSE;
for (t = 0; t <3; t++) {
if (Trans[t]) cmsFreeToneCurve(Trans[t]);
if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]);
}
cmsPipelineFree(LutPlusCurves);
cmsPipelineFree(OriginalLut);
*dwFlags &= ~cmsFLAGS_CAN_CHANGE_FORMATTER;
*Lut = OptimizedLUT;
*TransformFn = PerformanceEval8;
*UserData = p8;
*FreeDataFn = Performance8free;
return TRUE;
Error:
for (t = 0; t < 3; t++) {
if (Trans[t]) cmsFreeToneCurve(Trans[t]);
if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]);
}
if (LutPlusCurves != NULL) cmsPipelineFree(LutPlusCurves);
if (OptimizedLUT != NULL) cmsPipelineFree(OptimizedLUT);
return FALSE;
}