summaryrefslogtreecommitdiff
path: root/third_party/lcms/src/cmsopt.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/lcms/src/cmsopt.c')
-rw-r--r--third_party/lcms/src/cmsopt.c273
1 files changed, 208 insertions, 65 deletions
diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
index 76de01554c..abe26b93af 100644
--- a/third_party/lcms/src/cmsopt.c
+++ b/third_party/lcms/src/cmsopt.c
@@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2011 Marti Maria Saguer
+// Copyright (c) 1998-2016 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -162,6 +162,89 @@ cmsBool _Remove2Op(cmsPipeline* Lut, cmsStageSignature Op1, cmsStageSignature Op
return AnyOpt;
}
+
+static
+cmsBool CloseEnoughFloat(cmsFloat64Number a, cmsFloat64Number b)
+{
+ return fabs(b - a) < 0.00001f;
+}
+
+static
+cmsBool isFloatMatrixIdentity(const cmsMAT3* a)
+{
+ cmsMAT3 Identity;
+ int i, j;
+
+ _cmsMAT3identity(&Identity);
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (!CloseEnoughFloat(a->v[i].n[j], Identity.v[i].n[j])) return FALSE;
+
+ return TRUE;
+}
+// if two adjacent matrices are found, multiply them.
+static
+cmsBool _MultiplyMatrix(cmsPipeline* Lut)
+{
+ cmsStage** pt1;
+ cmsStage** pt2;
+ cmsStage* chain;
+ cmsBool AnyOpt = FALSE;
+
+ pt1 = &Lut->Elements;
+ if (*pt1 == NULL) return AnyOpt;
+
+ while (*pt1 != NULL) {
+
+ pt2 = &((*pt1)->Next);
+ if (*pt2 == NULL) return AnyOpt;
+
+ if ((*pt1)->Implements == cmsSigMatrixElemType && (*pt2)->Implements == cmsSigMatrixElemType) {
+
+ // Get both matrices
+ _cmsStageMatrixData* m1 = (_cmsStageMatrixData*) cmsStageData(*pt1);
+ _cmsStageMatrixData* m2 = (_cmsStageMatrixData*) cmsStageData(*pt2);
+ cmsMAT3 res;
+
+ // Input offset and output offset should be zero to use this optimization
+ if (m1->Offset != NULL || m2 ->Offset != NULL ||
+ cmsStageInputChannels(*pt1) != 3 || cmsStageOutputChannels(*pt1) != 3 ||
+ cmsStageInputChannels(*pt2) != 3 || cmsStageOutputChannels(*pt2) != 3)
+ return FALSE;
+
+ // Multiply both matrices to get the result
+ _cmsMAT3per(&res, (cmsMAT3*)m2->Double, (cmsMAT3*)m1->Double);
+
+ // Get the next in chain afer the matrices
+ chain = (*pt2)->Next;
+
+ // Remove both matrices
+ _RemoveElement(pt2);
+ _RemoveElement(pt1);
+
+ // Now what if the result is a plain identity?
+ if (!isFloatMatrixIdentity(&res)) {
+
+ // We can not get rid of full matrix
+ cmsStage* Multmat = cmsStageAllocMatrix(Lut->ContextID, 3, 3, (const cmsFloat64Number*) &res, NULL);
+ if (Multmat == NULL) return FALSE; // Should never happen
+
+ // Recover the chain
+ Multmat->Next = chain;
+ *pt1 = Multmat;
+ }
+
+ AnyOpt = TRUE;
+ }
+ else
+ pt1 = &((*pt1)->Next);
+ }
+
+ return AnyOpt;
+}
+
+
// Preoptimize just gets rif of no-ops coming paired. Conversion from v2 to v4 followed
// by a v4 to v2 and vice-versa. The elements are then discarded.
static
@@ -194,6 +277,9 @@ cmsBool PreOptimize(cmsPipeline* Lut)
// Remove float pcs Lab conversions
Opt |= _Remove2Op(Lut, cmsSigXYZ2FloatPCS, cmsSigFloatPCS2XYZ);
+ // Simplify matrix.
+ Opt |= _MultiplyMatrix(Lut);
+
if (Opt) AnyOpt = TRUE;
} while (Opt);
@@ -250,12 +336,12 @@ static
void* Prelin16dup(cmsContext ContextID, const void* ptr)
{
Prelin16Data* p16 = (Prelin16Data*) ptr;
- Prelin16Data* Duped = _cmsDupMem(ContextID, p16, sizeof(Prelin16Data));
+ Prelin16Data* Duped = (Prelin16Data*) _cmsDupMem(ContextID, p16, sizeof(Prelin16Data));
if (Duped == NULL) return NULL;
- Duped ->EvalCurveOut16 = (_cmsInterpFn16*)_cmsDupMem(ContextID, p16 ->EvalCurveOut16, p16 ->nOutputs * sizeof(_cmsInterpFn16));
- Duped ->ParamsCurveOut16 = (cmsInterpParams**)_cmsDupMem(ContextID, p16 ->ParamsCurveOut16, p16 ->nOutputs * sizeof(cmsInterpParams* ));
+ Duped->EvalCurveOut16 = (_cmsInterpFn16*) _cmsDupMem(ContextID, p16->EvalCurveOut16, p16->nOutputs * sizeof(_cmsInterpFn16));
+ Duped->ParamsCurveOut16 = (cmsInterpParams**)_cmsDupMem(ContextID, p16->ParamsCurveOut16, p16->nOutputs * sizeof(cmsInterpParams*));
return Duped;
}
@@ -268,7 +354,7 @@ Prelin16Data* PrelinOpt16alloc(cmsContext ContextID,
int nOutputs, cmsToneCurve** Out )
{
int i;
- Prelin16Data* p16 = _cmsMallocZero(ContextID, sizeof(Prelin16Data));
+ Prelin16Data* p16 = (Prelin16Data*)_cmsMallocZero(ContextID, sizeof(Prelin16Data));
if (p16 == NULL) return NULL;
p16 ->nInputs = nInputs;
@@ -380,10 +466,6 @@ cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[],
return FALSE;
}
- if (nChannelsIn != 1 && nChannelsIn != 3 && nChannelsIn != 4) {
- cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn);
- return FALSE;
- }
if (nChannelsIn == 4) {
px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0;
@@ -447,7 +529,7 @@ cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[],
return TRUE;
}
-// Auxiliar, to see if two values are equal or very different
+// Auxiliary, to see if two values are equal or very different
static
cmsBool WhitesAreEqual(int n, cmsUInt16Number White1[], cmsUInt16Number White2[] )
{
@@ -455,7 +537,7 @@ cmsBool WhitesAreEqual(int n, cmsUInt16Number White1[], cmsUInt16Number White2[]
for (i=0; i < n; i++) {
- if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremly different that the fixup should be avoided
+ if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremely different that the fixup should be avoided
if (White1[i] != White2[i]) return FALSE;
}
return TRUE;
@@ -593,7 +675,7 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
cmsStage* PreLin = cmsPipelineGetPtrToFirstStage(Src);
// Check if suitable
- if (PreLin ->Type == cmsSigCurveSetElemType) {
+ if (PreLin && PreLin ->Type == cmsSigCurveSetElemType) {
// Maybe this is a linear tram, so we can avoid the whole stuff
if (!AllCurvesAreLinear(PreLin)) {
@@ -626,7 +708,7 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
cmsStage* PostLin = cmsPipelineGetPtrToLastStage(Src);
// Check if suitable
- if (cmsStageType(PostLin) == cmsSigCurveSetElemType) {
+ if (PostLin && cmsStageType(PostLin) == cmsSigCurveSetElemType) {
// Maybe this is a linear tram, so we can avoid the whole stuff
if (!AllCurvesAreLinear(PostLin)) {
@@ -835,7 +917,7 @@ void PrelinEval8(register const cmsUInt16Number Input[],
Prelin8Data* p8 = (Prelin8Data*) D;
register const cmsInterpParams* p = p8 ->p;
int TotalOut = p -> nOutputs;
- const cmsUInt16Number* LutTable = (const cmsUInt16Number*)p -> Table;
+ const cmsUInt16Number* LutTable = (const cmsUInt16Number*) p->Table;
r = Input[0] >> 8;
g = Input[1] >> 8;
@@ -928,8 +1010,8 @@ cmsBool IsDegenerated(const cmsToneCurve* g)
}
if (Zeros == 1 && Poles == 1) return FALSE; // For linear tables
- if (Zeros > (nEntries / 4)) return TRUE; // Degenerated, mostly zeros
- if (Poles > (nEntries / 4)) return TRUE; // Degenerated, mostly poles
+ if (Zeros > (nEntries / 20)) return TRUE; // Degenerated, many zeros
+ if (Poles > (nEntries / 20)) return TRUE; // Degenerated, many poles
return FALSE;
}
@@ -951,17 +1033,19 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
cmsColorSpaceSignature ColorSpace, OutputColorSpace;
cmsStage* OptimizedPrelinMpe;
cmsStage* mpe;
- cmsToneCurve** OptimizedPrelinCurves;
- _cmsStageCLutData* OptimizedPrelinCLUT;
+ cmsToneCurve** OptimizedPrelinCurves;
+ _cmsStageCLutData* OptimizedPrelinCLUT;
// This is a loosy optimization! does not apply in floating-point cases
if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
- // Only on RGB
+ // Only on chunky RGB
if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE;
- if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE;
+ if (T_PLANAR(*InputFormat)) return FALSE;
+ if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE;
+ if (T_PLANAR(*OutputFormat)) return FALSE;
// On 16 bits, user has to specify the feature
if (!_cmsFormatterIs8bit(*InputFormat)) {
@@ -985,6 +1069,22 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
memset(Trans, 0, sizeof(Trans));
memset(TransReverse, 0, sizeof(TransReverse));
+ // If the last stage of the original lut are curves, and those curves are
+ // degenerated, it is likely the transform is squeezing and clipping
+ // the output from previous CLUT. We cannot optimize this case
+ {
+ cmsStage* last = cmsPipelineGetPtrToLastStage(OriginalLut);
+
+ if (cmsStageType(last) == cmsSigCurveSetElemType) {
+
+ _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*)cmsStageData(last);
+ for (i = 0; i < Data->nCurves; i++) {
+ if (IsDegenerated(Data->TheCurves[i]))
+ goto Error;
+ }
+ }
+ }
+
for (t = 0; t < OriginalLut ->InputChannels; t++) {
Trans[t] = cmsBuildTabulatedToneCurve16(OriginalLut ->ContextID, PRELINEARIZATION_POINTS, NULL);
if (Trans[t] == NULL) goto Error;
@@ -1159,10 +1259,10 @@ void* CurvesDup(cmsContext ContextID, const void* ptr)
if (Data == NULL) return NULL;
- Data ->Curves = (cmsUInt16Number**)_cmsDupMem(ContextID, Data ->Curves, Data ->nCurves * sizeof(cmsUInt16Number*));
+ Data->Curves = (cmsUInt16Number**) _cmsDupMem(ContextID, Data->Curves, Data->nCurves * sizeof(cmsUInt16Number*));
for (i=0; i < Data -> nCurves; i++) {
- Data ->Curves[i] = (cmsUInt16Number*)_cmsDupMem(ContextID, Data ->Curves[i], Data -> nElements * sizeof(cmsUInt16Number));
+ Data->Curves[i] = (cmsUInt16Number*) _cmsDupMem(ContextID, Data->Curves[i], Data->nElements * sizeof(cmsUInt16Number));
}
return (void*) Data;
@@ -1181,12 +1281,12 @@ Curves16Data* CurvesAlloc(cmsContext ContextID, int nCurves, int nElements, cmsT
c16 ->nCurves = nCurves;
c16 ->nElements = nElements;
- c16 ->Curves = (cmsUInt16Number**)_cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*));
+ c16->Curves = (cmsUInt16Number**) _cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*));
if (c16 ->Curves == NULL) return NULL;
for (i=0; i < nCurves; i++) {
- c16->Curves[i] = (cmsUInt16Number*)_cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number));
+ c16->Curves[i] = (cmsUInt16Number*) _cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number));
if (c16->Curves[i] == NULL) {
@@ -1318,7 +1418,10 @@ cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUI
GammaTables[i] = NULL;
}
- if (GammaTables != NULL) _cmsFree(Src ->ContextID, GammaTables);
+ if (GammaTables != NULL) {
+ _cmsFree(Src->ContextID, GammaTables);
+ GammaTables = NULL;
+ }
// Maybe the curves are linear at the end
if (!AllCurvesAreLinear(ObtainedCurves)) {
@@ -1543,55 +1646,89 @@ cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, c
// Fill function pointers
_cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper);
return TRUE;
-Error:
- _cmsFree(Dest->ContextID, p);
- return FALSE;
+ Error:
+ _cmsFree(Dest->ContextID, p);
+ return FALSE;
}
// 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast!
-// TODO: Allow a third matrix for abs. colorimetric
static
cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
{
- cmsStage* Curve1, *Curve2;
- cmsStage* Matrix1, *Matrix2;
- _cmsStageMatrixData* Data1;
- _cmsStageMatrixData* Data2;
- cmsMAT3 res;
- cmsBool IdentityMat;
- cmsPipeline* Dest, *Src;
+ cmsStage* Curve1, *Curve2;
+ cmsStage* Matrix1, *Matrix2;
+ cmsMAT3 res;
+ cmsBool IdentityMat;
+ cmsPipeline* Dest, *Src;
+ cmsFloat64Number* Offset;
- // Only works on RGB to RGB
- if (T_CHANNELS(*InputFormat) != 3 || T_CHANNELS(*OutputFormat) != 3) return FALSE;
+ // Only works on RGB to RGB
+ if (T_CHANNELS(*InputFormat) != 3 || T_CHANNELS(*OutputFormat) != 3) return FALSE;
- // Only works on 8 bit input
- if (!_cmsFormatterIs8bit(*InputFormat)) return FALSE;
+ // Only works on 8 bit input
+ if (!_cmsFormatterIs8bit(*InputFormat)) return FALSE;
- // Seems suitable, proceed
- Src = *Lut;
+ // Seems suitable, proceed
+ Src = *Lut;
- // Check for shaper-matrix-matrix-shaper structure, that is what this optimizer stands for
- if (!cmsPipelineCheckAndRetreiveStages(Src, 4,
- cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
- &Curve1, &Matrix1, &Matrix2, &Curve2)) return FALSE;
+ // Check for:
+ //
+ // shaper-matrix-matrix-shaper
+ // shaper-matrix-shaper
+ //
+ // Both of those constructs are possible (first because abs. colorimetric).
+ // additionally, In the first case, the input matrix offset should be zero.
- // Get both matrices
- Data1 = (_cmsStageMatrixData*) cmsStageData(Matrix1);
- Data2 = (_cmsStageMatrixData*) cmsStageData(Matrix2);
+ IdentityMat = FALSE;
+ if (cmsPipelineCheckAndRetreiveStages(Src, 4,
+ cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
+ &Curve1, &Matrix1, &Matrix2, &Curve2)) {
- // Input offset should be zero
- if (Data1 ->Offset != NULL) return FALSE;
+ // Get both matrices
+ _cmsStageMatrixData* Data1 = (_cmsStageMatrixData*)cmsStageData(Matrix1);
+ _cmsStageMatrixData* Data2 = (_cmsStageMatrixData*)cmsStageData(Matrix2);
- // Multiply both matrices to get the result
- _cmsMAT3per(&res, (cmsMAT3*) Data2 ->Double, (cmsMAT3*) Data1 ->Double);
+ // Input offset should be zero
+ if (Data1->Offset != NULL) return FALSE;
- // Now the result is in res + Data2 -> Offset. Maybe is a plain identity?
- IdentityMat = FALSE;
- if (_cmsMAT3isIdentity(&res) && Data2 ->Offset == NULL) {
+ // Multiply both matrices to get the result
+ _cmsMAT3per(&res, (cmsMAT3*)Data2->Double, (cmsMAT3*)Data1->Double);
- // We can get rid of full matrix
- IdentityMat = TRUE;
- }
+ // Only 2nd matrix has offset, or it is zero
+ Offset = Data2->Offset;
+
+ // Now the result is in res + Data2 -> Offset. Maybe is a plain identity?
+ if (_cmsMAT3isIdentity(&res) && Offset == NULL) {
+
+ // We can get rid of full matrix
+ IdentityMat = TRUE;
+ }
+
+ }
+ else {
+
+ if (cmsPipelineCheckAndRetreiveStages(Src, 3,
+ cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
+ &Curve1, &Matrix1, &Curve2)) {
+
+ _cmsStageMatrixData* Data = (_cmsStageMatrixData*)cmsStageData(Matrix1);
+
+ // Copy the matrix to our result
+ memcpy(&res, Data->Double, sizeof(res));
+
+ // Preserve the Odffset (may be NULL as a zero offset)
+ Offset = Data->Offset;
+
+ if (_cmsMAT3isIdentity(&res) && Offset == NULL) {
+
+ // We can get rid of full matrix
+ IdentityMat = TRUE;
+ }
+ }
+ else
+ return FALSE; // Not optimizeable this time
+
+ }
// Allocate an empty LUT
Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels);
@@ -1601,9 +1738,12 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1)))
goto Error;
- if (!IdentityMat)
- if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest ->ContextID, 3, 3, (const cmsFloat64Number*) &res, Data2 ->Offset)))
- goto Error;
+ if (!IdentityMat) {
+
+ if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest->ContextID, 3, 3, (const cmsFloat64Number*)&res, Offset)))
+ goto Error;
+ }
+
if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2)))
goto Error;
@@ -1616,12 +1756,12 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
_cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1);
_cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2);
- // In this particular optimization, cach?does not help as it takes more time to deal with
- // the cach?that with the pixel handling
+ // In this particular optimization, cache does not help as it takes more time to deal with
+ // the cache that with the pixel handling
*dwFlags |= cmsFLAGS_NOCACHE;
// Setup the optimizarion routines
- if (!SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Data2 ->Offset, mpeC2->TheCurves, OutputFormat))
+ if (!SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Offset, mpeC2->TheCurves, OutputFormat))
goto Error;
}
@@ -1809,3 +1949,6 @@ cmsBool _cmsOptimizePipeline(cmsContext ContextID,
// Only simple optimizations succeeded
return AnySuccess;
}
+
+
+