summaryrefslogtreecommitdiffstats
path: root/debian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c
diff options
context:
space:
mode:
authorMichele Calgaro <michele.calgaro@yahoo.it>2020-09-11 14:38:47 +0900
committerMichele Calgaro <michele.calgaro@yahoo.it>2020-09-11 14:38:47 +0900
commit884c8093d63402a1ad0b502244b791e3c6782be3 (patch)
treea600d4ab0d431a2bdfe4c15b70df43c14fbd8dd0 /debian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c
parent14e1aa2006796f147f3f4811fb908a6b01e79253 (diff)
downloadextra-dependencies-884c8093d63402a1ad0b502244b791e3c6782be3.tar.gz
extra-dependencies-884c8093d63402a1ad0b502244b791e3c6782be3.zip
Added debian extra dependency packages.
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'debian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c')
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c954
1 files changed, 954 insertions, 0 deletions
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c
new file mode 100755
index 00000000..857d82fc
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c
@@ -0,0 +1,954 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 Marti Maria
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+#include "lcms.h"
+
+// Gamma handling.
+
+LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries);
+void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma);
+void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3]);
+LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma);
+LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE Src);
+LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma);
+LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[]);
+LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma);
+LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma, int nPoints);
+LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda);
+
+LCMSBOOL cdecl _cmsSmoothEndpoints(LPWORD Table, int nPoints);
+
+
+// Sampled curves
+
+LPSAMPLEDCURVE cdecl cmsAllocSampledCurve(int nItems);
+void cdecl cmsFreeSampledCurve(LPSAMPLEDCURVE p);
+void cdecl cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max);
+void cdecl cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max);
+LCMSBOOL cdecl cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double SmoothingLambda);
+void cdecl cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints);
+
+LPSAMPLEDCURVE cdecl cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints);
+
+double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t);
+double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold);
+
+// ----------------------------------------------------------------------------------------
+
+
+#define MAX_KNOTS 4096
+typedef float vec[MAX_KNOTS+1];
+
+
+// Ciclic-redundant-check for assuring table is a true representation of parametric curve
+
+// The usual polynomial, which is used for AAL5, FDDI, and probably Ethernet
+#define QUOTIENT 0x04c11db7
+
+static
+unsigned int Crc32(unsigned int result, LPVOID ptr, int len)
+{
+ int i,j;
+ BYTE octet;
+ LPBYTE data = (LPBYTE) ptr;
+
+ for (i=0; i < len; i++) {
+
+ octet = *data++;
+
+ for (j=0; j < 8; j++) {
+
+ if (result & 0x80000000) {
+
+ result = (result << 1) ^ QUOTIENT ^ (octet >> 7);
+ }
+ else
+ {
+ result = (result << 1) ^ (octet >> 7);
+ }
+ octet <<= 1;
+ }
+ }
+
+ return result;
+}
+
+// Get CRC of gamma table
+
+unsigned int _cmsCrc32OfGammaTable(LPGAMMATABLE Table)
+{
+ unsigned int crc = ~0U;
+
+ crc = Crc32(crc, &Table -> Seed.Type, sizeof(int));
+ crc = Crc32(crc, Table ->Seed.Params, sizeof(double)*10);
+ crc = Crc32(crc, &Table ->nEntries, sizeof(int));
+ crc = Crc32(crc, Table ->GammaTable, sizeof(WORD) * Table -> nEntries);
+
+ return ~crc;
+
+}
+
+
+LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries)
+{
+ LPGAMMATABLE p;
+ size_t size;
+
+ if (nEntries > 65530 || nEntries <= 0) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't create gammatable of more than 65530 entries");
+ return NULL;
+ }
+
+ size = sizeof(GAMMATABLE) + (sizeof(WORD) * (nEntries-1));
+
+ p = (LPGAMMATABLE) _cmsMalloc(size);
+ if (!p) return NULL;
+
+ ZeroMemory(p, size);
+
+ p -> Seed.Type = 0;
+ p -> nEntries = nEntries;
+
+ return p;
+}
+
+void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma)
+{
+ if (Gamma) _cmsFree(Gamma);
+}
+
+
+
+void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3])
+{
+ cmsFreeGamma(Gamma[0]);
+ cmsFreeGamma(Gamma[1]);
+ cmsFreeGamma(Gamma[2]);
+ Gamma[0] = Gamma[1] = Gamma[2] = NULL;
+}
+
+
+
+// Duplicate a gamma table
+
+LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE In)
+{
+ LPGAMMATABLE Ptr;
+ size_t size;
+
+ Ptr = cmsAllocGamma(In -> nEntries);
+ if (Ptr == NULL) return NULL;
+
+ size = sizeof(GAMMATABLE) + (sizeof(WORD) * (In -> nEntries-1));
+
+ CopyMemory(Ptr, In, size);
+ return Ptr;
+}
+
+
+// Handle gamma using interpolation tables. The resulting curves can become
+// very stange, but are pleasent to eye.
+
+LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma,
+ LPGAMMATABLE OutGamma)
+{
+ register int i;
+ L16PARAMS L16In, L16Out;
+ LPWORD InPtr, OutPtr;
+ LPGAMMATABLE p;
+
+ p = cmsAllocGamma(256);
+ if (!p) return NULL;
+
+ cmsCalcL16Params(InGamma -> nEntries, &L16In);
+ InPtr = InGamma -> GammaTable;
+
+ cmsCalcL16Params(OutGamma -> nEntries, &L16Out);
+ OutPtr = OutGamma-> GammaTable;
+
+ for (i=0; i < 256; i++)
+ {
+ WORD wValIn, wValOut;
+
+ wValIn = cmsLinearInterpLUT16(RGB_8_TO_16(i), InPtr, &L16In);
+ wValOut = cmsReverseLinearInterpLUT16(wValIn, OutPtr, &L16Out);
+
+ p -> GammaTable[i] = wValOut;
+ }
+
+ return p;
+}
+
+
+
+// New method, using smoothed parametric curves. This works FAR better.
+// We want to get
+//
+// y = f(g^-1(x)) ; f = ingamma, g = outgamma
+//
+// And this can be parametrized as
+//
+// y = f(t)
+// x = g(t)
+
+
+LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma,
+ LPGAMMATABLE OutGamma, int nPoints)
+{
+
+ LPSAMPLEDCURVE x, y, r;
+ LPGAMMATABLE res;
+
+ x = cmsConvertGammaToSampledCurve(InGamma, nPoints);
+ y = cmsConvertGammaToSampledCurve(OutGamma, nPoints);
+ r = cmsJoinSampledCurves(y, x, nPoints);
+
+ // Does clean "hair"
+ cmsSmoothSampledCurve(r, 0.001);
+
+ cmsClampSampledCurve(r, 0.0, 65535.0);
+
+ cmsFreeSampledCurve(x);
+ cmsFreeSampledCurve(y);
+
+ res = cmsConvertSampledCurveToGamma(r, 65535.0);
+ cmsFreeSampledCurve(r);
+
+ return res;
+}
+
+
+
+// Reverse a gamma table
+
+LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma)
+{
+ register int i;
+ L16PARAMS L16In;
+ LPWORD InPtr;
+ LPGAMMATABLE p;
+
+ // Try to reverse it analytically whatever possible
+ if (InGamma -> Seed.Type > 0 && InGamma -> Seed.Type <= 5 &&
+ _cmsCrc32OfGammaTable(InGamma) == InGamma -> Seed.Crc32) {
+
+ return cmsBuildParametricGamma(nResultSamples, -(InGamma -> Seed.Type), InGamma ->Seed.Params);
+ }
+
+
+ // Nope, reverse the table
+ p = cmsAllocGamma(nResultSamples);
+ if (!p) return NULL;
+
+ cmsCalcL16Params(InGamma -> nEntries, &L16In);
+ InPtr = InGamma -> GammaTable;
+
+ for (i=0; i < nResultSamples; i++)
+ {
+ WORD wValIn, wValOut;
+
+ wValIn = _cmsQuantizeVal(i, nResultSamples);
+ wValOut = cmsReverseLinearInterpLUT16(wValIn, InPtr, &L16In);
+ p -> GammaTable[i] = wValOut;
+ }
+
+
+ return p;
+}
+
+
+
+// Parametric curves
+//
+// Parameters goes as: Gamma, a, b, c, d, e, f
+// Type is the ICC type +1
+// if type is negative, then the curve is analyticaly inverted
+
+LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[])
+{
+ LPGAMMATABLE Table;
+ double R, Val, dval, e;
+ int i;
+ int ParamsByType[] = { 0, 1, 3, 4, 5, 7 };
+
+ Table = cmsAllocGamma(nEntries);
+ if (NULL == Table) return NULL;
+
+ Table -> Seed.Type = Type;
+
+ CopyMemory(Table ->Seed.Params, Params, ParamsByType[abs(Type)] * sizeof(double));
+
+
+ for (i=0; i < nEntries; i++) {
+
+ R = (double) i / (nEntries-1);
+
+ switch (Type) {
+
+ // X = Y ^ Gamma
+ case 1:
+ Val = pow(R, Params[0]);
+ break;
+
+ // Type 1 Reversed: X = Y ^1/gamma
+ case -1:
+ Val = pow(R, 1/Params[0]);
+ break;
+
+ // CIE 122-1966
+ // Y = (aX + b)^Gamma | X >= -b/a
+ // Y = 0 | else
+ case 2:
+ if (R >= -Params[2] / Params[1]) {
+
+ e = Params[1]*R + Params[2];
+
+ if (e > 0)
+ Val = pow(e, Params[0]);
+ else
+ Val = 0;
+ }
+ else
+ Val = 0;
+ break;
+
+ // Type 2 Reversed
+ // X = (Y ^1/g - b) / a
+ case -2:
+
+ Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
+ if (Val < 0)
+ Val = 0;
+ break;
+
+
+ // IEC 61966-3
+ // Y = (aX + b)^Gamma | X <= -b/a
+ // Y = c | else
+ case 3:
+ if (R >= -Params[2] / Params[1]) {
+
+ e = Params[1]*R + Params[2];
+ Val = pow(e, Params[0]) + Params[3];
+ }
+ else
+ Val = Params[3];
+ break;
+
+
+ // Type 3 reversed
+ // X=((Y-c)^1/g - b)/a | (Y>=c)
+ // X=-b/a | (Y<c)
+
+ case -3:
+ if (R >= Params[3]) {
+ e = R - Params[3];
+ Val = (pow(e, 1/Params[0]) - Params[2]) / Params[1];
+ if (Val < 0) Val = 0;
+ }
+ else {
+ Val = -Params[2] / Params[1];
+ }
+ break;
+
+
+ // IEC 61966-2.1 (sRGB)
+ // Y = (aX + b)^Gamma | X >= d
+ // Y = cX | X < d
+ case 4:
+ if (R >= Params[4]) {
+
+ e = Params[1]*R + Params[2];
+ if (e > 0)
+ Val = pow(e, Params[0]);
+ else
+ Val = 0;
+ }
+ else
+ Val = R * Params[3];
+ break;
+
+ // Type 4 reversed
+ // X=((Y^1/g-b)/a) | Y >= (ad+b)^g
+ // X=Y/c | Y< (ad+b)^g
+
+ case -4:
+ if (R >= pow(Params[1] * Params[4] + Params[2], Params[0])) {
+
+ Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
+ }
+ else {
+ Val = R / Params[3];
+ }
+ break;
+
+
+
+ // Y = (aX + b)^Gamma + e | X <= d
+ // Y = cX + f | else
+ case 5:
+ if (R >= Params[4]) {
+
+ e = Params[1]*R + Params[2];
+ Val = pow(e, Params[0]) + Params[5];
+ }
+ else
+ Val = R*Params[3] + Params[6];
+ break;
+
+
+ // Reversed type 5
+ // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e)
+ // X=(Y-f)/c | else
+ case -5:
+
+ if (R >= pow(Params[1] * Params[4], Params[0]) + Params[5]) {
+
+ Val = pow(R - Params[5], 1/Params[0]) - Params[2] / Params[1];
+ }
+ else {
+ Val = (R - Params[6]) / Params[3];
+ }
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported parametric curve type=%d", abs(Type)-1);
+ cmsFreeGamma(Table);
+ return NULL;
+ }
+
+
+ // Saturate
+
+ dval = Val * 65535.0 + .5;
+ if (dval > 65535.) dval = 65535.0;
+ if (dval < 0) dval = 0;
+
+ Table->GammaTable[i] = (WORD) floor(dval);
+ }
+
+ Table -> Seed.Crc32 = _cmsCrc32OfGammaTable(Table);
+
+ return Table;
+}
+
+// Build a gamma table based on gamma constant
+
+LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma)
+{
+ return cmsBuildParametricGamma(nEntries, 1, &Gamma);
+}
+
+
+
+// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite
+// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press.
+//
+// Smoothing and interpolation with second differences.
+//
+// Input: weights (w), data (y): vector from 1 to m.
+// Input: smoothing parameter (lambda), length (m).
+// Output: smoothed vector (z): vector from 1 to m.
+
+
+static
+void smooth2(vec w, vec y, vec z, float lambda, int m)
+{
+ int i, i1, i2;
+ vec c, d, e;
+ d[1] = w[1] + lambda;
+ c[1] = -2 * lambda / d[1];
+ e[1] = lambda /d[1];
+ z[1] = w[1] * y[1];
+ d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1];
+ c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2];
+ e[2] = lambda / d[2];
+ z[2] = w[2] * y[2] - c[1] * z[1];
+ for (i = 3; i < m - 1; i++) {
+ i1 = i - 1; i2 = i - 2;
+ d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
+ c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i];
+ e[i] = lambda / d[i];
+ z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2];
+ }
+ i1 = m - 2; i2 = m - 3;
+ d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
+ c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1];
+ z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2];
+ i1 = m - 1; i2 = m - 2;
+ d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
+ z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m];
+ z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m];
+ for (i = m - 2; 1<= i; i--)
+ z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2];
+}
+
+
+
+// Smooths a curve sampled at regular intervals
+
+LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda)
+
+{
+ vec w, y, z;
+ int i, nItems, Zeros, Poles;
+
+
+ if (cmsIsLinear(Tab->GammaTable, Tab->nEntries)) return FALSE; // Nothing to do
+
+ nItems = Tab -> nEntries;
+
+ if (nItems > MAX_KNOTS) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothGamma: too many points.");
+ return FALSE;
+ }
+
+ ZeroMemory(w, nItems * sizeof(float));
+ ZeroMemory(y, nItems * sizeof(float));
+ ZeroMemory(z, nItems * sizeof(float));
+
+ for (i=0; i < nItems; i++)
+ {
+ y[i+1] = (float) Tab -> GammaTable[i];
+ w[i+1] = 1.0;
+ }
+
+ smooth2(w, y, z, (float) lambda, nItems);
+
+ // Do some reality - checking...
+ Zeros = Poles = 0;
+ for (i=nItems; i > 1; --i) {
+
+ if (z[i] == 0.) Zeros++;
+ if (z[i] >= 65535.) Poles++;
+ if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
+ }
+
+ if (Zeros > (nItems / 3)) return FALSE; // Degenerated, mostly zeros
+ if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles
+
+ // Seems ok
+
+ for (i=0; i < nItems; i++) {
+
+ // Clamp to WORD
+
+ float v = z[i+1];
+
+ if (v < 0) v = 0;
+ if (v > 65535.) v = 65535.;
+
+ Tab -> GammaTable[i] = (WORD) floor(v + .5);
+ }
+
+ return TRUE;
+}
+
+
+// Check if curve is exponential, return gamma if so.
+
+double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold)
+{
+ double gamma, sum, sum2;
+ double n, x, y, Std;
+ int i;
+
+ sum = sum2 = n = 0;
+
+ // Does exclude endpoints
+ for (i=1; i < nEntries - 1; i++) {
+
+ x = (double) i / (nEntries - 1);
+ y = (double) GammaTable[i] / 65535.;
+
+ // Avoid 7% on lower part to prevent
+ // artifacts due to linear ramps
+
+ if (y > 0. && y < 1. && x > 0.07) {
+
+ gamma = log(y) / log(x);
+ sum += gamma;
+ sum2 += gamma * gamma;
+ n++;
+ }
+
+ }
+
+ // Take a look on SD to see if gamma isn't exponential at all
+ Std = sqrt((n * sum2 - sum * sum) / (n*(n-1)));
+
+
+ if (Std > Thereshold)
+ return -1.0;
+
+ return (sum / n); // The mean
+}
+
+
+double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t)
+{
+ return cmsEstimateGammaEx(t->GammaTable, t->nEntries, 0.7);
+}
+
+
+// -----------------------------------------------------------------Sampled curves
+
+// Allocate a empty curve
+
+LPSAMPLEDCURVE cmsAllocSampledCurve(int nItems)
+{
+ LPSAMPLEDCURVE pOut;
+
+ pOut = (LPSAMPLEDCURVE) _cmsMalloc(sizeof(SAMPLEDCURVE));
+ if (pOut == NULL)
+ return NULL;
+
+ if((pOut->Values = (double *) _cmsMalloc(nItems * sizeof(double))) == NULL)
+ {
+ _cmsFree(pOut);
+ return NULL;
+ }
+
+ pOut->nItems = nItems;
+ ZeroMemory(pOut->Values, nItems * sizeof(double));
+
+ return pOut;
+}
+
+
+void cmsFreeSampledCurve(LPSAMPLEDCURVE p)
+{
+ _cmsFree((LPVOID) p -> Values);
+ _cmsFree((LPVOID) p);
+}
+
+
+
+// Does duplicate a sampled curve
+
+LPSAMPLEDCURVE cmsDupSampledCurve(LPSAMPLEDCURVE p)
+{
+ LPSAMPLEDCURVE out;
+
+ out = cmsAllocSampledCurve(p -> nItems);
+ if (!out) return NULL;
+
+ CopyMemory(out ->Values, p ->Values, p->nItems * sizeof(double));
+
+ return out;
+}
+
+
+// Take min, max of curve
+
+void cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max)
+{
+ int i;
+
+ *Min = 65536.;
+ *Max = 0.;
+
+ for (i=0; i < p -> nItems; i++) {
+
+ double v = p -> Values[i];
+
+ if (v < *Min)
+ *Min = v;
+
+ if (v > *Max)
+ *Max = v;
+ }
+
+ if (*Min < 0) *Min = 0;
+ if (*Max > 65535.0) *Max = 65535.0;
+}
+
+// Clamps to Min, Max
+
+void cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max)
+{
+
+ int i;
+
+ for (i=0; i < p -> nItems; i++) {
+
+ double v = p -> Values[i];
+
+ if (v < Min)
+ v = Min;
+
+ if (v > Max)
+ v = Max;
+
+ p -> Values[i] = v;
+
+ }
+
+}
+
+
+
+// Smooths a curve sampled at regular intervals
+
+LCMSBOOL cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double lambda)
+{
+ vec w, y, z;
+ int i, nItems;
+
+ nItems = Tab -> nItems;
+
+ if (nItems > MAX_KNOTS) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothSampledCurve: too many points.");
+ return FALSE;
+ }
+
+ ZeroMemory(w, nItems * sizeof(float));
+ ZeroMemory(y, nItems * sizeof(float));
+ ZeroMemory(z, nItems * sizeof(float));
+
+ for (i=0; i < nItems; i++)
+ {
+ float value = (float) Tab -> Values[i];
+
+ y[i+1] = value;
+ w[i+1] = (float) ((value < 0.0) ? 0 : 1);
+ }
+
+
+ smooth2(w, y, z, (float) lambda, nItems);
+
+ for (i=0; i < nItems; i++) {
+
+ Tab -> Values[i] = z[i+1];;
+ }
+
+ return TRUE;
+
+}
+
+
+// Scale a value v, within domain Min .. Max
+// to a domain 0..(nPoints-1)
+
+static
+double ScaleVal(double v, double Min, double Max, int nPoints)
+{
+
+ double a, b;
+
+ if (v <= Min) return 0;
+ if (v >= Max) return (nPoints-1);
+
+ a = (double) (nPoints - 1) / (Max - Min);
+ b = a * Min;
+
+ return (a * v) - b;
+
+}
+
+
+// Does rescale a sampled curve to fit in a 0..(nPoints-1) domain
+
+void cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints)
+{
+
+ int i;
+
+ for (i=0; i < p -> nItems; i++) {
+
+ double v = p -> Values[i];
+
+ p -> Values[i] = ScaleVal(v, Min, Max, nPoints);
+ }
+
+}
+
+
+// Joins two sampled curves for X and Y. Curves should be sorted.
+
+LPSAMPLEDCURVE cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints)
+{
+ int i, j;
+ LPSAMPLEDCURVE out;
+ double MinX, MinY, MaxX, MaxY;
+ double x, y, x1, y1, x2, y2, a, b;
+
+ out = cmsAllocSampledCurve(nResultingPoints);
+ if (out == NULL)
+ return NULL;
+
+ if (X -> nItems != Y -> nItems) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsJoinSampledCurves: invalid curve.");
+ cmsFreeSampledCurve(out);
+ return NULL;
+ }
+
+ // Get endpoints of sampled curves
+ cmsEndpointsOfSampledCurve(X, &MinX, &MaxX);
+ cmsEndpointsOfSampledCurve(Y, &MinY, &MaxY);
+
+
+ // Set our points
+ out ->Values[0] = MinY;
+ for (i=1; i < nResultingPoints; i++) {
+
+ // Scale t to x domain
+ x = (i * (MaxX - MinX) / (nResultingPoints-1)) + MinX;
+
+ // Find interval in which j is within (always up,
+ // since fn should be monotonic at all)
+
+ j = 1;
+ while ((j < X ->nItems - 1) && X ->Values[j] < x)
+ j++;
+
+ // Now x is within X[j-1], X[j]
+ x1 = X ->Values[j-1]; x2 = X ->Values[j];
+ y1 = Y ->Values[j-1]; y2 = Y ->Values[j];
+
+ // Interpolate the value
+ a = (y1 - y2) / (x1 - x2);
+ b = y1 - a * x1;
+ y = a* x + b;
+
+ out ->Values[i] = y;
+ }
+
+
+ cmsClampSampledCurve(out, MinY, MaxY);
+ return out;
+}
+
+
+
+// Convert between curve types
+
+LPGAMMATABLE cmsConvertSampledCurveToGamma(LPSAMPLEDCURVE Sampled, double Max)
+{
+ LPGAMMATABLE Gamma;
+ int i, nPoints;
+
+
+ nPoints = Sampled ->nItems;
+
+ Gamma = cmsAllocGamma(nPoints);
+ for (i=0; i < nPoints; i++) {
+
+ Gamma->GammaTable[i] = (WORD) floor(ScaleVal(Sampled ->Values[i], 0, Max, 65536) + .5);
+ }
+
+ return Gamma;
+
+}
+
+// Inverse of anterior
+
+LPSAMPLEDCURVE cmsConvertGammaToSampledCurve(LPGAMMATABLE Gamma, int nPoints)
+{
+ LPSAMPLEDCURVE Sampled;
+ L16PARAMS L16;
+ int i;
+ WORD wQuant, wValIn;
+
+ if (nPoints > 4096) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsConvertGammaToSampledCurve: too many points (max=4096)");
+ return NULL;
+ }
+
+ cmsCalcL16Params(Gamma -> nEntries, &L16);
+
+ Sampled = cmsAllocSampledCurve(nPoints);
+ for (i=0; i < nPoints; i++) {
+ wQuant = _cmsQuantizeVal(i, nPoints);
+ wValIn = cmsLinearInterpLUT16(wQuant, Gamma ->GammaTable, &L16);
+ Sampled ->Values[i] = (float) wValIn;
+ }
+
+ return Sampled;
+}
+
+
+
+
+// Smooth endpoints (used in Black/White compensation)
+
+LCMSBOOL _cmsSmoothEndpoints(LPWORD Table, int nEntries)
+{
+ vec w, y, z;
+ int i, Zeros, Poles;
+
+
+
+ if (cmsIsLinear(Table, nEntries)) return FALSE; // Nothing to do
+
+
+ if (nEntries > MAX_KNOTS) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "_cmsSmoothEndpoints: too many points.");
+ return FALSE;
+ }
+
+ ZeroMemory(w, nEntries * sizeof(float));
+ ZeroMemory(y, nEntries * sizeof(float));
+ ZeroMemory(z, nEntries * sizeof(float));
+
+ for (i=0; i < nEntries; i++)
+ {
+ y[i+1] = (float) Table[i];
+ w[i+1] = 1.0;
+ }
+
+ w[1] = 65535.0;
+ w[nEntries] = 65535.0;
+
+ smooth2(w, y, z, (float) nEntries, nEntries);
+
+ // Do some reality - checking...
+ Zeros = Poles = 0;
+ for (i=nEntries; i > 1; --i) {
+
+ if (z[i] == 0.) Zeros++;
+ if (z[i] >= 65535.) Poles++;
+ if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
+ }
+
+ if (Zeros > (nEntries / 3)) return FALSE; // Degenerated, mostly zeros
+ if (Poles > (nEntries / 3)) return FALSE; // Degenerated, mostly poles
+
+ // Seems ok
+
+ for (i=0; i < nEntries; i++) {
+
+ // Clamp to WORD
+
+ float v = z[i+1];
+
+ if (v < 0) v = 0;
+ if (v > 65535.) v = 65535.;
+
+ Table[i] = (WORD) floor(v + .5);
+ }
+
+ return TRUE;
+}