3 // Copyright (C) 1998-2007 Marti Maria
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the Software
10 // is furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
17 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 LPGAMMATABLE LCMSEXPORT
cmsAllocGamma(int nEntries
);
29 void LCMSEXPORT
cmsFreeGamma(LPGAMMATABLE Gamma
);
30 void LCMSEXPORT
cmsFreeGammaTriple(LPGAMMATABLE Gamma
[3]);
31 LPGAMMATABLE LCMSEXPORT
cmsBuildGamma(int nEntries
, double Gamma
);
32 LPGAMMATABLE LCMSEXPORT
cmsDupGamma(LPGAMMATABLE Src
);
33 LPGAMMATABLE LCMSEXPORT
cmsReverseGamma(int nResultSamples
, LPGAMMATABLE InGamma
);
34 LPGAMMATABLE LCMSEXPORT
cmsBuildParametricGamma(int nEntries
, int Type
, double Params
[]);
35 LPGAMMATABLE LCMSEXPORT
cmsJoinGamma(LPGAMMATABLE InGamma
, LPGAMMATABLE OutGamma
);
36 LPGAMMATABLE LCMSEXPORT
cmsJoinGammaEx(LPGAMMATABLE InGamma
, LPGAMMATABLE OutGamma
, int nPoints
);
37 LCMSBOOL LCMSEXPORT
cmsSmoothGamma(LPGAMMATABLE Tab
, double lambda
);
39 LCMSBOOL
cdecl _cmsSmoothEndpoints(LPWORD Table
, int nPoints
);
44 LPSAMPLEDCURVE
cdecl cmsAllocSampledCurve(int nItems
);
45 void cdecl cmsFreeSampledCurve(LPSAMPLEDCURVE p
);
46 void cdecl cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p
, double* Min
, double* Max
);
47 void cdecl cmsClampSampledCurve(LPSAMPLEDCURVE p
, double Min
, double Max
);
48 LCMSBOOL
cdecl cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab
, double SmoothingLambda
);
49 void cdecl cmsRescaleSampledCurve(LPSAMPLEDCURVE p
, double Min
, double Max
, int nPoints
);
51 LPSAMPLEDCURVE
cdecl cmsJoinSampledCurves(LPSAMPLEDCURVE X
, LPSAMPLEDCURVE Y
, int nResultingPoints
);
53 double LCMSEXPORT
cmsEstimateGamma(LPGAMMATABLE t
);
54 double LCMSEXPORT
cmsEstimateGammaEx(LPWORD GammaTable
, int nEntries
, double Thereshold
);
56 // ----------------------------------------------------------------------------------------
59 #define MAX_KNOTS 4096
60 typedef float vec
[MAX_KNOTS
+1];
63 // Ciclic-redundant-check for assuring table is a true representation of parametric curve
65 // The usual polynomial, which is used for AAL5, FDDI, and probably Ethernet
66 #define QUOTIENT 0x04c11db7
69 unsigned int Crc32(unsigned int result
, LPVOID ptr
, int len
)
73 LPBYTE data
= (LPBYTE
) ptr
;
75 for (i
=0; i
< len
; i
++) {
79 for (j
=0; j
< 8; j
++) {
81 if (result
& 0x80000000) {
83 result
= (result
<< 1) ^ QUOTIENT
^ (octet
>> 7);
87 result
= (result
<< 1) ^ (octet
>> 7);
96 // Get CRC of gamma table
98 unsigned int _cmsCrc32OfGammaTable(LPGAMMATABLE Table
)
100 unsigned int crc
= ~0U;
102 crc
= Crc32(crc
, &Table
-> Seed
.Type
, sizeof(int));
103 crc
= Crc32(crc
, Table
->Seed
.Params
, sizeof(double)*10);
104 crc
= Crc32(crc
, &Table
->nEntries
, sizeof(int));
105 crc
= Crc32(crc
, Table
->GammaTable
, sizeof(WORD
) * Table
-> nEntries
);
112 LPGAMMATABLE LCMSEXPORT
cmsAllocGamma(int nEntries
)
117 if (nEntries
> 65530 || nEntries
< 0) {
118 cmsSignalError(LCMS_ERRC_ABORTED
, "Couldn't create gammatable of more than 65530 entries");
122 size
= sizeof(GAMMATABLE
) + (sizeof(WORD
) * (nEntries
-1));
124 p
= (LPGAMMATABLE
) _cmsMalloc(size
);
130 p
-> nEntries
= nEntries
;
135 void LCMSEXPORT
cmsFreeGamma(LPGAMMATABLE Gamma
)
137 if (Gamma
) _cmsFree(Gamma
);
142 void LCMSEXPORT
cmsFreeGammaTriple(LPGAMMATABLE Gamma
[3])
144 cmsFreeGamma(Gamma
[0]);
145 cmsFreeGamma(Gamma
[1]);
146 cmsFreeGamma(Gamma
[2]);
147 Gamma
[0] = Gamma
[1] = Gamma
[2] = NULL
;
152 // Duplicate a gamma table
154 LPGAMMATABLE LCMSEXPORT
cmsDupGamma(LPGAMMATABLE In
)
159 Ptr
= cmsAllocGamma(In
-> nEntries
);
160 if (Ptr
== NULL
) return NULL
;
162 size
= sizeof(GAMMATABLE
) + (sizeof(WORD
) * (In
-> nEntries
-1));
164 CopyMemory(Ptr
, In
, size
);
169 // Handle gamma using interpolation tables. The resulting curves can become
170 // very stange, but are pleasent to eye.
172 LPGAMMATABLE LCMSEXPORT
cmsJoinGamma(LPGAMMATABLE InGamma
,
173 LPGAMMATABLE OutGamma
)
176 L16PARAMS L16In
, L16Out
;
177 LPWORD InPtr
, OutPtr
;
180 p
= cmsAllocGamma(256);
183 cmsCalcL16Params(InGamma
-> nEntries
, &L16In
);
184 InPtr
= InGamma
-> GammaTable
;
186 cmsCalcL16Params(OutGamma
-> nEntries
, &L16Out
);
187 OutPtr
= OutGamma
-> GammaTable
;
189 for (i
=0; i
< 256; i
++)
191 WORD wValIn
, wValOut
;
193 wValIn
= cmsLinearInterpLUT16(RGB_8_TO_16(i
), InPtr
, &L16In
);
194 wValOut
= cmsReverseLinearInterpLUT16(wValIn
, OutPtr
, &L16Out
);
196 p
-> GammaTable
[i
] = wValOut
;
204 // New method, using smoothed parametric curves. This works FAR better.
207 // y = f(g^-1(x)) ; f = ingamma, g = outgamma
209 // And this can be parametrized as
215 LPGAMMATABLE LCMSEXPORT
cmsJoinGammaEx(LPGAMMATABLE InGamma
,
216 LPGAMMATABLE OutGamma
, int nPoints
)
219 LPSAMPLEDCURVE x
, y
, r
;
222 x
= cmsConvertGammaToSampledCurve(InGamma
, nPoints
);
223 y
= cmsConvertGammaToSampledCurve(OutGamma
, nPoints
);
224 r
= cmsJoinSampledCurves(y
, x
, nPoints
);
227 cmsSmoothSampledCurve(r
, 0.001);
229 cmsClampSampledCurve(r
, 0.0, 65535.0);
231 cmsFreeSampledCurve(x
);
232 cmsFreeSampledCurve(y
);
234 res
= cmsConvertSampledCurveToGamma(r
, 65535.0);
235 cmsFreeSampledCurve(r
);
242 // Reverse a gamma table
244 LPGAMMATABLE LCMSEXPORT
cmsReverseGamma(int nResultSamples
, LPGAMMATABLE InGamma
)
251 // Try to reverse it analytically whatever possible
252 if (InGamma
-> Seed
.Type
> 0 && InGamma
-> Seed
.Type
<= 5 &&
253 _cmsCrc32OfGammaTable(InGamma
) == InGamma
-> Seed
.Crc32
) {
255 return cmsBuildParametricGamma(nResultSamples
, -(InGamma
-> Seed
.Type
), InGamma
->Seed
.Params
);
259 // Nope, reverse the table
260 p
= cmsAllocGamma(nResultSamples
);
263 cmsCalcL16Params(InGamma
-> nEntries
, &L16In
);
264 InPtr
= InGamma
-> GammaTable
;
266 for (i
=0; i
< nResultSamples
; i
++)
268 WORD wValIn
, wValOut
;
270 wValIn
= _cmsQuantizeVal(i
, nResultSamples
);
271 wValOut
= cmsReverseLinearInterpLUT16(wValIn
, InPtr
, &L16In
);
272 p
-> GammaTable
[i
] = wValOut
;
283 // Parameters goes as: Gamma, a, b, c, d, e, f
284 // Type is the ICC type +1
285 // if type is negative, then the curve is analyticaly inverted
287 LPGAMMATABLE LCMSEXPORT
cmsBuildParametricGamma(int nEntries
, int Type
, double Params
[])
290 double R
, Val
, dval
, e
;
292 int ParamsByType
[] = { 0, 1, 3, 4, 5, 7 };
294 Table
= cmsAllocGamma(nEntries
);
295 if (NULL
== Table
) return NULL
;
297 Table
-> Seed
.Type
= Type
;
299 CopyMemory(Table
->Seed
.Params
, Params
, ParamsByType
[abs(Type
)] * sizeof(double));
302 for (i
=0; i
< nEntries
; i
++) {
304 R
= (double) i
/ (nEntries
-1);
310 Val
= pow(R
, Params
[0]);
313 // Type 1 Reversed: X = Y ^1/gamma
315 Val
= pow(R
, 1/Params
[0]);
319 // Y = (aX + b)^Gamma | X >= -b/a
322 if (R
>= -Params
[2] / Params
[1]) {
324 e
= Params
[1]*R
+ Params
[2];
327 Val
= pow(e
, Params
[0]);
336 // X = (Y ^1/g - b) / a
339 Val
= (pow(R
, 1.0/Params
[0]) - Params
[2]) / Params
[1];
346 // Y = (aX + b)^Gamma | X <= -b/a
349 if (R
>= -Params
[2] / Params
[1]) {
351 e
= Params
[1]*R
+ Params
[2];
352 Val
= pow(e
, Params
[0]) + Params
[3];
360 // X=((Y-c)^1/g - b)/a | (Y>=c)
364 if (R
>= Params
[3]) {
366 Val
= (pow(e
, 1/Params
[0]) - Params
[2]) / Params
[1];
367 if (Val
< 0) Val
= 0;
370 Val
= -Params
[2] / Params
[1];
375 // IEC 61966-2.1 (sRGB)
376 // Y = (aX + b)^Gamma | X >= d
379 if (R
>= Params
[4]) {
381 e
= Params
[1]*R
+ Params
[2];
383 Val
= pow(e
, Params
[0]);
392 // X=((Y^1/g-b)/a) | Y >= (ad+b)^g
393 // X=Y/c | Y< (ad+b)^g
396 if (R
>= pow(Params
[1] * Params
[4] + Params
[2], Params
[0])) {
398 Val
= (pow(R
, 1.0/Params
[0]) - Params
[2]) / Params
[1];
407 // Y = (aX + b)^Gamma + e | X <= d
410 if (R
>= Params
[4]) {
412 e
= Params
[1]*R
+ Params
[2];
413 Val
= pow(e
, Params
[0]) + Params
[5];
416 Val
= R
*Params
[3] + Params
[6];
421 // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e)
425 if (R
>= pow(Params
[1] * Params
[4], Params
[0]) + Params
[5]) {
427 Val
= pow(R
- Params
[5], 1/Params
[0]) - Params
[2] / Params
[1];
430 Val
= (R
- Params
[6]) / Params
[3];
435 cmsSignalError(LCMS_ERRC_ABORTED
, "Unsupported parametric curve type=%d", abs(Type
)-1);
443 dval
= Val
* 65535.0 + .5;
444 if (dval
> 65535.) dval
= 65535.0;
445 if (dval
< 0) dval
= 0;
447 Table
->GammaTable
[i
] = (WORD
) floor(dval
);
450 Table
-> Seed
.Crc32
= _cmsCrc32OfGammaTable(Table
);
455 // Build a gamma table based on gamma constant
457 LPGAMMATABLE LCMSEXPORT
cmsBuildGamma(int nEntries
, double Gamma
)
459 return cmsBuildParametricGamma(nEntries
, 1, &Gamma
);
464 // From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite
465 // differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press.
467 // Smoothing and interpolation with second differences.
469 // Input: weights (w), data (y): vector from 1 to m.
470 // Input: smoothing parameter (lambda), length (m).
471 // Output: smoothed vector (z): vector from 1 to m.
475 void smooth2(vec w
, vec y
, vec z
, float lambda
, int m
)
479 d
[1] = w
[1] + lambda
;
480 c
[1] = -2 * lambda
/ d
[1];
483 d
[2] = w
[2] + 5 * lambda
- d
[1] * c
[1] * c
[1];
484 c
[2] = (-4 * lambda
- d
[1] * c
[1] * e
[1]) / d
[2];
485 e
[2] = lambda
/ d
[2];
486 z
[2] = w
[2] * y
[2] - c
[1] * z
[1];
487 for (i
= 3; i
< m
- 1; i
++) {
488 i1
= i
- 1; i2
= i
- 2;
489 d
[i
]= w
[i
] + 6 * lambda
- c
[i1
] * c
[i1
] * d
[i1
] - e
[i2
] * e
[i2
] * d
[i2
];
490 c
[i
] = (-4 * lambda
-d
[i1
] * c
[i1
] * e
[i1
])/ d
[i
];
491 e
[i
] = lambda
/ d
[i
];
492 z
[i
] = w
[i
] * y
[i
] - c
[i1
] * z
[i1
] - e
[i2
] * z
[i2
];
494 i1
= m
- 2; i2
= m
- 3;
495 d
[m
- 1] = w
[m
- 1] + 5 * lambda
-c
[i1
] * c
[i1
] * d
[i1
] - e
[i2
] * e
[i2
] * d
[i2
];
496 c
[m
- 1] = (-2 * lambda
- d
[i1
] * c
[i1
] * e
[i1
]) / d
[m
- 1];
497 z
[m
- 1] = w
[m
- 1] * y
[m
- 1] - c
[i1
] * z
[i1
] - e
[i2
] * z
[i2
];
498 i1
= m
- 1; i2
= m
- 2;
499 d
[m
] = w
[m
] + lambda
- c
[i1
] * c
[i1
] * d
[i1
] - e
[i2
] * e
[i2
] * d
[i2
];
500 z
[m
] = (w
[m
] * y
[m
] - c
[i1
] * z
[i1
] - e
[i2
] * z
[i2
]) / d
[m
];
501 z
[m
- 1] = z
[m
- 1] / d
[m
- 1] - c
[m
- 1] * z
[m
];
502 for (i
= m
- 2; 1<= i
; i
--)
503 z
[i
] = z
[i
] / d
[i
] - c
[i
] * z
[i
+ 1] - e
[i
] * z
[i
+ 2];
508 // Smooths a curve sampled at regular intervals
510 LCMSBOOL LCMSEXPORT
cmsSmoothGamma(LPGAMMATABLE Tab
, double lambda
)
514 int i
, nItems
, Zeros
, Poles
;
517 if (cmsIsLinear(Tab
->GammaTable
, Tab
->nEntries
)) return FALSE
; // Nothing to do
519 nItems
= Tab
-> nEntries
;
521 if (nItems
> MAX_KNOTS
) {
522 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsSmoothGamma: too many points.");
526 ZeroMemory(w
, nItems
* sizeof(float));
527 ZeroMemory(y
, nItems
* sizeof(float));
528 ZeroMemory(z
, nItems
* sizeof(float));
530 for (i
=0; i
< nItems
; i
++)
532 y
[i
+1] = (float) Tab
-> GammaTable
[i
];
536 smooth2(w
, y
, z
, (float) lambda
, nItems
);
538 // Do some reality - checking...
540 for (i
=nItems
; i
> 1; --i
) {
542 if (z
[i
] == 0.) Zeros
++;
543 if (z
[i
] >= 65535.) Poles
++;
544 if (z
[i
] < z
[i
-1]) return FALSE
; // Non-Monotonic
547 if (Zeros
> (nItems
/ 3)) return FALSE
; // Degenerated, mostly zeros
548 if (Poles
> (nItems
/ 3)) return FALSE
; // Degenerated, mostly poles
552 for (i
=0; i
< nItems
; i
++) {
559 if (v
> 65535.) v
= 65535.;
561 Tab
-> GammaTable
[i
] = (WORD
) floor(v
+ .5);
568 // Check if curve is exponential, return gamma if so.
570 double LCMSEXPORT
cmsEstimateGammaEx(LPWORD GammaTable
, int nEntries
, double Thereshold
)
572 double gamma
, sum
, sum2
;
578 // Does exclude endpoints
579 for (i
=1; i
< nEntries
- 1; i
++) {
581 x
= (double) i
/ (nEntries
- 1);
582 y
= (double) GammaTable
[i
] / 65535.;
584 // Avoid 7% on lower part to prevent
585 // artifacts due to linear ramps
587 if (y
> 0. && y
< 1. && x
> 0.07) {
589 gamma
= log(y
) / log(x
);
591 sum2
+= gamma
* gamma
;
597 // Take a look on SD to see if gamma isn't exponential at all
598 Std
= sqrt((n
* sum2
- sum
* sum
) / (n
*(n
-1)));
601 if (Std
> Thereshold
)
604 return (sum
/ n
); // The mean
608 double LCMSEXPORT
cmsEstimateGamma(LPGAMMATABLE t
)
610 return cmsEstimateGammaEx(t
->GammaTable
, t
->nEntries
, 0.7);
614 // -----------------------------------------------------------------Sampled curves
616 // Allocate a empty curve
618 LPSAMPLEDCURVE
cmsAllocSampledCurve(int nItems
)
622 pOut
= (LPSAMPLEDCURVE
) _cmsMalloc(sizeof(SAMPLEDCURVE
));
626 if((pOut
->Values
= (double *) _cmsMalloc(nItems
* sizeof(double))) == NULL
)
632 pOut
->nItems
= nItems
;
633 ZeroMemory(pOut
->Values
, nItems
* sizeof(double));
639 void cmsFreeSampledCurve(LPSAMPLEDCURVE p
)
641 _cmsFree((LPVOID
) p
-> Values
);
642 _cmsFree((LPVOID
) p
);
647 // Does duplicate a sampled curve
649 LPSAMPLEDCURVE
cmsDupSampledCurve(LPSAMPLEDCURVE p
)
653 out
= cmsAllocSampledCurve(p
-> nItems
);
654 if (!out
) return NULL
;
656 CopyMemory(out
->Values
, p
->Values
, p
->nItems
* sizeof(double));
662 // Take min, max of curve
664 void cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p
, double* Min
, double* Max
)
671 for (i
=0; i
< p
-> nItems
; i
++) {
673 double v
= p
-> Values
[i
];
682 if (*Min
< 0) *Min
= 0;
683 if (*Max
> 65535.0) *Max
= 65535.0;
686 // Clamps to Min, Max
688 void cmsClampSampledCurve(LPSAMPLEDCURVE p
, double Min
, double Max
)
693 for (i
=0; i
< p
-> nItems
; i
++) {
695 double v
= p
-> Values
[i
];
711 // Smooths a curve sampled at regular intervals
713 LCMSBOOL
cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab
, double lambda
)
718 nItems
= Tab
-> nItems
;
720 if (nItems
> MAX_KNOTS
) {
721 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsSmoothSampledCurve: too many points.");
725 ZeroMemory(w
, nItems
* sizeof(float));
726 ZeroMemory(y
, nItems
* sizeof(float));
727 ZeroMemory(z
, nItems
* sizeof(float));
729 for (i
=0; i
< nItems
; i
++)
731 float value
= (float) Tab
-> Values
[i
];
734 w
[i
+1] = (float) ((value
< 0.0) ? 0 : 1);
738 smooth2(w
, y
, z
, (float) lambda
, nItems
);
740 for (i
=0; i
< nItems
; i
++) {
742 Tab
-> Values
[i
] = z
[i
+1];;
750 // Scale a value v, within domain Min .. Max
751 // to a domain 0..(nPoints-1)
754 double ScaleVal(double v
, double Min
, double Max
, int nPoints
)
759 if (v
<= Min
) return 0;
760 if (v
>= Max
) return (nPoints
-1);
762 a
= (double) (nPoints
- 1) / (Max
- Min
);
770 // Does rescale a sampled curve to fit in a 0..(nPoints-1) domain
772 void cmsRescaleSampledCurve(LPSAMPLEDCURVE p
, double Min
, double Max
, int nPoints
)
777 for (i
=0; i
< p
-> nItems
; i
++) {
779 double v
= p
-> Values
[i
];
781 p
-> Values
[i
] = ScaleVal(v
, Min
, Max
, nPoints
);
787 // Joins two sampled curves for X and Y. Curves should be sorted.
789 LPSAMPLEDCURVE
cmsJoinSampledCurves(LPSAMPLEDCURVE X
, LPSAMPLEDCURVE Y
, int nResultingPoints
)
793 double MinX
, MinY
, MaxX
, MaxY
;
794 double x
, y
, x1
, y1
, x2
, y2
, a
, b
;
796 out
= cmsAllocSampledCurve(nResultingPoints
);
800 if (X
-> nItems
!= Y
-> nItems
) {
802 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsJoinSampledCurves: invalid curve.");
803 cmsFreeSampledCurve(out
);
807 // Get endpoints of sampled curves
808 cmsEndpointsOfSampledCurve(X
, &MinX
, &MaxX
);
809 cmsEndpointsOfSampledCurve(Y
, &MinY
, &MaxY
);
813 out
->Values
[0] = MinY
;
814 for (i
=1; i
< nResultingPoints
; i
++) {
816 // Scale t to x domain
817 x
= (i
* (MaxX
- MinX
) / (nResultingPoints
-1)) + MinX
;
819 // Find interval in which j is within (always up,
820 // since fn should be monotonic at all)
823 while ((j
< X
->nItems
- 1) && X
->Values
[j
] < x
)
826 // Now x is within X[j-1], X[j]
827 x1
= X
->Values
[j
-1]; x2
= X
->Values
[j
];
828 y1
= Y
->Values
[j
-1]; y2
= Y
->Values
[j
];
830 // Interpolate the value
831 a
= (y1
- y2
) / (x1
- x2
);
839 cmsClampSampledCurve(out
, MinY
, MaxY
);
845 // Convert between curve types
847 LPGAMMATABLE
cmsConvertSampledCurveToGamma(LPSAMPLEDCURVE Sampled
, double Max
)
853 nPoints
= Sampled
->nItems
;
855 Gamma
= cmsAllocGamma(nPoints
);
856 for (i
=0; i
< nPoints
; i
++) {
858 Gamma
->GammaTable
[i
] = (WORD
) floor(ScaleVal(Sampled
->Values
[i
], 0, Max
, 65536) + .5);
865 // Inverse of anterior
867 LPSAMPLEDCURVE
cmsConvertGammaToSampledCurve(LPGAMMATABLE Gamma
, int nPoints
)
869 LPSAMPLEDCURVE Sampled
;
874 if (nPoints
> 4096) {
876 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsConvertGammaToSampledCurve: too many points (max=4096)");
880 cmsCalcL16Params(Gamma
-> nEntries
, &L16
);
882 Sampled
= cmsAllocSampledCurve(nPoints
);
883 for (i
=0; i
< nPoints
; i
++) {
884 wQuant
= _cmsQuantizeVal(i
, nPoints
);
885 wValIn
= cmsLinearInterpLUT16(wQuant
, Gamma
->GammaTable
, &L16
);
886 Sampled
->Values
[i
] = (float) wValIn
;
895 // Smooth endpoints (used in Black/White compensation)
897 LCMSBOOL
_cmsSmoothEndpoints(LPWORD Table
, int nEntries
)
904 if (cmsIsLinear(Table
, nEntries
)) return FALSE
; // Nothing to do
907 if (nEntries
> MAX_KNOTS
) {
908 cmsSignalError(LCMS_ERRC_ABORTED
, "_cmsSmoothEndpoints: too many points.");
912 ZeroMemory(w
, nEntries
* sizeof(float));
913 ZeroMemory(y
, nEntries
* sizeof(float));
914 ZeroMemory(z
, nEntries
* sizeof(float));
916 for (i
=0; i
< nEntries
; i
++)
918 y
[i
+1] = (float) Table
[i
];
923 w
[nEntries
] = 65535.0;
925 smooth2(w
, y
, z
, (float) nEntries
, nEntries
);
927 // Do some reality - checking...
929 for (i
=nEntries
; i
> 1; --i
) {
931 if (z
[i
] == 0.) Zeros
++;
932 if (z
[i
] >= 65535.) Poles
++;
933 if (z
[i
] < z
[i
-1]) return FALSE
; // Non-Monotonic
936 if (Zeros
> (nEntries
/ 3)) return FALSE
; // Degenerated, mostly zeros
937 if (Poles
> (nEntries
/ 3)) return FALSE
; // Degenerated, mostly poles
941 for (i
=0; i
< nEntries
; i
++) {
948 if (v
> 65535.) v
= 65535.;
950 Table
[i
] = (WORD
) floor(v
+ .5);