| /* |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| // This file is available under and governed by the GNU General Public |
| // License version 2 only, as published by the Free Software Foundation. |
| // However, the following notice accompanied the original version of this |
| // file: |
| // |
| // |
| // 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; |
| } |