#include "fast_float_internal.h"
typedef cmsInt32Number cmsS1Fixed14Number;
#define DOUBLE_TO_1FIXED14(x) ((cmsS1Fixed14Number) floor((x) * 16384.0 + 0.5))
typedef struct {
cmsS1Fixed14Number Mat[4][4];
void * real_ptr;
cmsContext ContextID;
cmsS1Fixed14Number Shaper1R[256]; cmsS1Fixed14Number Shaper1G[256];
cmsS1Fixed14Number Shaper1B[256];
cmsUInt8Number Shaper2R[0x4001]; cmsUInt8Number Shaper2G[0x4001];
cmsUInt8Number Shaper2B[0x4001];
} XMatShaper8Data;
static
XMatShaper8Data* malloc_aligned(cmsContext ContextID)
{
cmsUInt8Number* real_ptr = (cmsUInt8Number*) _cmsMallocZero(ContextID, sizeof(XMatShaper8Data) + 32);
cmsUInt8Number* aligned = (cmsUInt8Number*) (((uintptr_t)real_ptr + 16) & ~0xf);
XMatShaper8Data* p = (XMatShaper8Data*) aligned;
p ->real_ptr = real_ptr;
return p;
}
static
void free_aligned(XMatShaper8Data* a)
{
_cmsFree(a->ContextID, a->real_ptr);
}
static
void FreeMatShaper(cmsContext ContextID, void* Data)
{
UNUSED_PARAMETER(ContextID);
if (Data != NULL) free_aligned((XMatShaper8Data*) Data);
}
static
void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve)
{
int i;
cmsFloat32Number R, y;
for (i=0; i < 256; i++) {
R = (cmsFloat32Number) (i / 255.0);
y = cmsEvalToneCurveFloat(Curve, R);
Table[i] = DOUBLE_TO_1FIXED14(y);
}
}
static
void FillSecondShaper(cmsUInt8Number* Table, cmsToneCurve* Curve)
{
int i;
cmsFloat32Number R, Val;
cmsInt32Number w;
for (i=0; i < 0x4001; i++) {
R = (cmsFloat32Number) (i / 16384.0f);
Val = cmsEvalToneCurveFloat(Curve, R);
w = (cmsInt32Number) (Val * 255.0f + 0.5f);
if (w < 0) w = 0;
if (w > 255) w = 255;
Table[i] = (cmsInt8Number) w;
}
}
static
XMatShaper8Data* SetMatShaper(cmsContext ContextID, cmsToneCurve* Curve1[3], cmsMAT3* Mat, cmsVEC3* Off, cmsToneCurve* Curve2[3])
{
XMatShaper8Data* p;
int i, j;
p = malloc_aligned(ContextID);
if (p == NULL) return FALSE;
p -> ContextID = ContextID;
FillFirstShaper(p ->Shaper1R, Curve1[0]);
FillFirstShaper(p ->Shaper1G, Curve1[1]);
FillFirstShaper(p ->Shaper1B, Curve1[2]);
FillSecondShaper(p ->Shaper2R, Curve2[0]);
FillSecondShaper(p ->Shaper2G, Curve2[1]);
FillSecondShaper(p ->Shaper2B, Curve2[2]);
for (i=0; i < 3; i++) {
for (j=0; j < 3; j++) {
p ->Mat[j][i] = DOUBLE_TO_1FIXED14(Mat->v[i].n[j]);
}
}
for (i=0; i < 3; i++) {
if (Off == NULL) {
p->Mat[3][i] = DOUBLE_TO_1FIXED14(0.5);
}
else {
p->Mat[3][i] = DOUBLE_TO_1FIXED14(Off->n[i] + 0.5);
}
}
return p;
}
static
void MatShaperXform8(struct _cmstransform_struct *CMMcargo,
const void* Input,
void* Output,
cmsUInt32Number PixelsPerLine,
cmsUInt32Number LineCount,
const cmsStride* Stride)
{
XMatShaper8Data* p = (XMatShaper8Data*) _cmsGetTransformUserData(CMMcargo);
cmsS1Fixed14Number l1, l2, l3;
cmsS1Fixed14Number r, g, b;
cmsUInt32Number ri, gi, bi;
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;
cmsUInt8Number* rout;
cmsUInt8Number* gout;
cmsUInt8Number* bout;
cmsUInt8Number* aout = 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;
rout = (cmsUInt8Number*)Output + DestStartingOrder[0] + strideOut;
gout = (cmsUInt8Number*)Output + DestStartingOrder[1] + strideOut;
bout = (cmsUInt8Number*)Output + DestStartingOrder[2] + strideOut;
if (nalpha)
aout = (cmsUInt8Number*)Output + DestStartingOrder[3] + strideOut;
for (ii = 0; ii < PixelsPerLine; ii++) {
r = p->Shaper1R[*rin];
g = p->Shaper1G[*gin];
b = p->Shaper1B[*bin];
l1 = (p->Mat[0][0] * r + p->Mat[1][0] * g + p->Mat[2][0] * b + p->Mat[3][0]) >> 14;
l2 = (p->Mat[0][1] * r + p->Mat[1][1] * g + p->Mat[2][1] * b + p->Mat[3][1]) >> 14;
l3 = (p->Mat[0][2] * r + p->Mat[1][2] * g + p->Mat[2][2] * b + p->Mat[3][2]) >> 14;
ri = (l1 < 0) ? 0 : ((l1 > 0x4000) ? 0x4000 : l1);
gi = (l2 < 0) ? 0 : ((l2 > 0x4000) ? 0x4000 : l2);
bi = (l3 < 0) ? 0 : ((l3 > 0x4000) ? 0x4000 : l3);
*rout = p->Shaper2R[ri];
*gout = p->Shaper2G[gi];
*bout = p->Shaper2B[bi];
if (ain) {
*aout = *ain;
}
rin += SourceIncrements[0];
gin += SourceIncrements[1];
bin += SourceIncrements[2];
if (ain) ain += SourceIncrements[3];
rout += DestIncrements[0];
gout += DestIncrements[1];
bout += DestIncrements[2];
if (aout) aout += DestIncrements[3];
}
strideIn += Stride->BytesPerLineIn;
strideOut += Stride->BytesPerLineOut;
}
}
cmsBool Optimize8MatrixShaper(_cmsTransform2Fn* TransformFn,
void** UserData,
_cmsFreeUserDataFn* FreeUserData,
cmsPipeline** Lut,
cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat,
cmsUInt32Number* dwFlags)
{
cmsStage* Curve1, *Curve2;
cmsStage* Matrix1, *Matrix2;
_cmsStageMatrixData* Data1;
_cmsStageMatrixData* Data2;
cmsMAT3 res;
cmsBool IdentityMat = FALSE;
cmsPipeline* Dest, *Src;
cmsContext ContextID;
cmsUInt32Number nChans;
cmsFloat64Number factor = 1.0;
if ( !( (T_CHANNELS(*InputFormat) == 3 && T_CHANNELS(*OutputFormat) == 3) ||
(T_CHANNELS(*InputFormat) == 1 && T_CHANNELS(*OutputFormat) == 1) )) return FALSE;
if (T_BYTES(*InputFormat) != 1 || T_BYTES(*OutputFormat) != 1) return FALSE;
Src = *Lut;
if (!cmsPipelineCheckAndRetreiveStages(Src, 4,
cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
&Curve1, &Matrix1, &Matrix2, &Curve2)) return FALSE;
ContextID = cmsGetPipelineContextID(Src);
nChans = T_CHANNELS(*InputFormat);
Data1 = (_cmsStageMatrixData*) cmsStageData(Matrix1);
Data2 = (_cmsStageMatrixData*) cmsStageData(Matrix2);
if (Data1 ->Offset != NULL) return FALSE;
if (cmsStageInputChannels(Matrix1) == 1 && cmsStageOutputChannels(Matrix2) == 1)
{
factor = Data1->Double[0]*Data2->Double[0] +
Data1->Double[1]*Data2->Double[1] +
Data1->Double[2]*Data2->Double[2];
if (fabs(1 - factor) < (1.0 / 65535.0)) IdentityMat = TRUE;
}
else
{
_cmsMAT3per(&res, (cmsMAT3*) Data2 ->Double, (cmsMAT3*) Data1 ->Double);
IdentityMat = FALSE;
if (_cmsMAT3isIdentity(&res) && Data2 ->Offset == NULL) {
IdentityMat = TRUE;
}
}
Dest = cmsPipelineAlloc(ContextID, nChans, nChans);
if (!Dest) return FALSE;
cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1));
if (!IdentityMat) {
if (nChans == 1)
cmsPipelineInsertStage(Dest, cmsAT_END,
cmsStageAllocMatrix(ContextID, 1, 1, (const cmsFloat64Number*) &factor, Data2->Offset));
else
cmsPipelineInsertStage(Dest, cmsAT_END,
cmsStageAllocMatrix(ContextID, 3, 3, (const cmsFloat64Number*) &res, Data2 ->Offset));
}
cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2));
if (IdentityMat) {
Optimize8ByJoiningCurves(TransformFn, UserData, FreeUserData, &Dest, InputFormat, OutputFormat, dwFlags);
}
else {
_cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1);
_cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2);
*dwFlags |= cmsFLAGS_NOCACHE;
*UserData = SetMatShaper(ContextID, mpeC1 ->TheCurves, &res, (cmsVEC3*) Data2 ->Offset, mpeC2->TheCurves);
*FreeUserData = FreeMatShaper;
*TransformFn = MatShaperXform8;
}
*dwFlags &= ~cmsFLAGS_CAN_CHANGE_FORMATTER;
cmsPipelineFree(Src);
*Lut = Dest;
return TRUE;
}