blob: 1b223c6134281de181a032ddbd17401797166233 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation. Sun designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Sun in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21 * CA 95054 USA or visit www.sun.com if you need additional information or
22 * have any questions.
23 */
24
25// This file is available under and governed by the GNU General Public
26// License version 2 only, as published by the Free Software Foundation.
27// However, the following notice accompanied the original version of this
28// file:
29//
30//
31// Little cms
32// Copyright (C) 1998-2006 Marti Maria
33//
34// Permission is hereby granted, free of charge, to any person obtaining
35// a copy of this software and associated documentation files (the "Software"),
36// to deal in the Software without restriction, including without limitation
37// the rights to use, copy, modify, merge, publish, distribute, sublicense,
38// and/or sell copies of the Software, and to permit persons to whom the Software
39// is furnished to do so, subject to the following conditions:
40//
41// The above copyright notice and this permission notice shall be included in
42// all copies or substantial portions of the Software.
43//
44// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
45// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
46// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
47// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
48// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
49// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
50// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
51
52
53#include "lcms.h"
54
55// Gamma handling.
56
57LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries);
58void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma);
59void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3]);
60LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma);
61LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE Src);
62LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma);
63LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[]);
64LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma);
65LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma, int nPoints);
66BOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda);
67
68BOOL cdecl _cmsSmoothEndpoints(LPWORD Table, int nPoints);
69
70
71// Sampled curves
72
73LPSAMPLEDCURVE cdecl cmsAllocSampledCurve(int nItems);
74void cdecl cmsFreeSampledCurve(LPSAMPLEDCURVE p);
75void cdecl cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max);
76void cdecl cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max);
77BOOL cdecl cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double SmoothingLambda);
78void cdecl cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints);
79
80LPSAMPLEDCURVE cdecl cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints);
81
82double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t);
83double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold);
84
85// ----------------------------------------------------------------------------------------
86
87// #define DEBUG 1
88
89#define MAX_KNOTS 4096
90typedef float vec[MAX_KNOTS+1];
91
92
93// Ciclic-redundant-check for assuring table is a true representation of parametric curve
94
95// The usual polynomial, which is used for AAL5, FDDI, and probably Ethernet
96#define QUOTIENT 0x04c11db7
97
98static
99unsigned int Crc32(unsigned int result, LPVOID ptr, int len)
100{
101 int i,j;
102 BYTE octet;
103 LPBYTE data = (LPBYTE) ptr;
104
105 for (i=0; i < len; i++) {
106
107 octet = *data++;
108
109 for (j=0; j < 8; j++) {
110
111 if (result & 0x80000000) {
112
113 result = (result << 1) ^ QUOTIENT ^ (octet >> 7);
114 }
115 else
116 {
117 result = (result << 1) ^ (octet >> 7);
118 }
119 octet <<= 1;
120 }
121 }
122
123 return result;
124}
125
126// Get CRC of gamma table
127
128unsigned int _cmsCrc32OfGammaTable(LPGAMMATABLE Table)
129{
130 unsigned int crc = ~0U;
131
132 crc = Crc32(crc, &Table -> Seed.Type, sizeof(int));
133 crc = Crc32(crc, Table ->Seed.Params, sizeof(double)*10);
134 crc = Crc32(crc, &Table ->nEntries, sizeof(int));
135 crc = Crc32(crc, Table ->GammaTable, sizeof(WORD) * Table -> nEntries);
136
137 return ~crc;
138
139}
140
141
142LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries)
143{
144 LPGAMMATABLE p;
145 size_t size;
146
147 if (nEntries > 65530) {
148 cmsSignalError(LCMS_ERRC_WARNING, "Couldn't create gammatable of more than 65530 entries; 65530 assumed");
149 nEntries = 65530;
150 }
151
152 size = sizeof(GAMMATABLE) + (sizeof(WORD) * (nEntries-1));
153
154 p = (LPGAMMATABLE) malloc(size);
155 if (!p) return NULL;
156
157 ZeroMemory(p, size);
158
159 p -> Seed.Type = 0;
160 p -> nEntries = nEntries;
161
162 return p;
163}
164
165void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma)
166{
167 if (Gamma) free(Gamma);
168}
169
170
171
172void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3])
173{
174 cmsFreeGamma(Gamma[0]);
175 cmsFreeGamma(Gamma[1]);
176 cmsFreeGamma(Gamma[2]);
177 Gamma[0] = Gamma[1] = Gamma[2] = NULL;
178}
179
180
181
182// Duplicate a gamma table
183
184LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE In)
185{
186 LPGAMMATABLE Ptr;
187 size_t size;
188
189 Ptr = cmsAllocGamma(In -> nEntries);
190 if (Ptr == NULL) return NULL;
191
192 size = sizeof(GAMMATABLE) + (sizeof(WORD) * (In -> nEntries-1));
193
194 CopyMemory(Ptr, In, size);
195 return Ptr;
196}
197
198
199// Handle gamma using interpolation tables. The resulting curves can become
200// very stange, but are pleasent to eye.
201
202LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma,
203 LPGAMMATABLE OutGamma)
204{
205 register int i;
206 L16PARAMS L16In, L16Out;
207 LPWORD InPtr, OutPtr;
208 LPGAMMATABLE p;
209
210 p = cmsAllocGamma(256);
211 if (!p) return NULL;
212
213 cmsCalcL16Params(InGamma -> nEntries, &L16In);
214 InPtr = InGamma -> GammaTable;
215
216 cmsCalcL16Params(OutGamma -> nEntries, &L16Out);
217 OutPtr = OutGamma-> GammaTable;
218
219 for (i=0; i < 256; i++)
220 {
221 WORD wValIn, wValOut;
222
223 wValIn = cmsLinearInterpLUT16(RGB_8_TO_16(i), InPtr, &L16In);
224 wValOut = cmsReverseLinearInterpLUT16(wValIn, OutPtr, &L16Out);
225
226 p -> GammaTable[i] = wValOut;
227 }
228
229 return p;
230}
231
232
233
234// New method, using smoothed parametric curves. This works FAR better.
235// We want to get
236//
237// y = f(g^-1(x)) ; f = ingamma, g = outgamma
238//
239// And this can be parametrized as
240//
241// y = f(t)
242// x = g(t)
243
244
245LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma,
246 LPGAMMATABLE OutGamma, int nPoints)
247{
248
249 LPSAMPLEDCURVE x, y, r;
250 LPGAMMATABLE res;
251
252 x = cmsConvertGammaToSampledCurve(InGamma, nPoints);
253 y = cmsConvertGammaToSampledCurve(OutGamma, nPoints);
254 r = cmsJoinSampledCurves(y, x, nPoints);
255
256 // Does clean "hair"
257 cmsSmoothSampledCurve(r, 0.001);
258
259 cmsClampSampledCurve(r, 0.0, 65535.0);
260
261 cmsFreeSampledCurve(x);
262 cmsFreeSampledCurve(y);
263
264 res = cmsConvertSampledCurveToGamma(r, 65535.0);
265 cmsFreeSampledCurve(r);
266
267 return res;
268}
269
270
271
272// Reverse a gamma table
273
274LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma)
275{
276 register int i;
277 L16PARAMS L16In;
278 LPWORD InPtr;
279 LPGAMMATABLE p;
280
281 p = cmsAllocGamma(nResultSamples);
282 if (!p) return NULL;
283
284 cmsCalcL16Params(InGamma -> nEntries, &L16In);
285 InPtr = InGamma -> GammaTable;
286
287 for (i=0; i < nResultSamples; i++)
288 {
289 WORD wValIn, wValOut;
290
291 wValIn = _cmsQuantizeVal(i, nResultSamples);
292 wValOut = cmsReverseLinearInterpLUT16(wValIn, InPtr, &L16In);
293 p -> GammaTable[i] = wValOut;
294 }
295
296
297 return p;
298}
299
300
301
302// Parametric curves
303//
304// Parameters goes as: Gamma, a, b, c, d, e, f
305// Type is the ICC type +1
306// if type is negative, then the curve is analyticaly inverted
307
308LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[])
309{
310 LPGAMMATABLE Table;
311 double R, Val, dval, e;
312 int i;
313 int ParamsByType[] = { 0, 1, 3, 4, 5, 7 };
314
315 Table = cmsAllocGamma(nEntries);
316 if (NULL == Table) return NULL;
317
318 Table -> Seed.Type = Type;
319
320 CopyMemory(Table ->Seed.Params, Params, ParamsByType[abs(Type)] * sizeof(double));
321
322
323 for (i=0; i < nEntries; i++) {
324
325 R = (double) i / (nEntries-1);
326
327 switch (Type) {
328
329 // X = Y ^ Gamma
330 case 1:
331 Val = pow(R, Params[0]);
332 break;
333
334 // Type 1 Reversed: X = Y ^1/gamma
335 case -1:
336 Val = pow(R, 1/Params[0]);
337 break;
338
339 // CIE 122-1966
340 // Y = (aX + b)^Gamma | X >= -b/a
341 // Y = 0 | else
342 case 2:
343 if (R >= -Params[2] / Params[1]) {
344
345 e = Params[1]*R + Params[2];
346
347 if (e > 0)
348 Val = pow(e, Params[0]);
349 else
350 Val = 0;
351 }
352 else
353 Val = 0;
354 break;
355
356 // Type 2 Reversed
357 // X = (Y ^1/g - b) / a
358 case -2:
359
360 Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
361 if (Val < 0)
362 Val = 0;
363 break;
364
365
366 // IEC 61966-3
367 // Y = (aX + b)^Gamma | X <= -b/a
368 // Y = c | else
369 case 3:
370 if (R >= -Params[2] / Params[1]) {
371
372 e = Params[1]*R + Params[2];
373 Val = pow(e, Params[0]) + Params[3];
374 }
375 else
376 Val = Params[3];
377 break;
378
379
380 // Type 3 reversed
381 // X=((Y-c)^1/g - b)/a | (Y>=c)
382 // X=-b/a | (Y<c)
383
384 case -3:
385 if (R >= Params[3]) {
386 e = R - Params[3];
387 Val = (pow(e, 1/Params[0]) - Params[2]) / Params[1];
388 if (Val < 0) Val = 0;
389 }
390 else {
391 Val = -Params[2] / Params[1];
392 }
393 break;
394
395
396 // IEC 61966-2.1 (sRGB)
397 // Y = (aX + b)^Gamma | X >= d
398 // Y = cX | X < d
399 case 4:
400 if (R >= Params[4]) {
401
402 e = Params[1]*R + Params[2];
403 if (e > 0)
404 Val = pow(e, Params[0]);
405 else
406 Val = 0;
407 }
408 else
409 Val = R * Params[3];
410 break;
411
412 // Type 4 reversed
413 // X=((Y^1/g-b)/a) | Y >= (ad+b)^g
414 // X=Y/c | Y< (ad+b)^g
415
416 case -4:
417 if (R >= pow(Params[1] * Params[4] + Params[2], Params[0])) {
418
419 Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
420 }
421 else {
422 Val = R / Params[3];
423 }
424 break;
425
426
427
428 // Y = (aX + b)^Gamma + e | X <= d
429 // Y = cX + f | else
430 case 5:
431 if (R >= Params[4]) {
432
433 e = Params[1]*R + Params[2];
434 Val = pow(e, Params[0]) + Params[5];
435 }
436 else
437 Val = R*Params[3] + Params[6];
438 break;
439
440
441 // Reversed type 5
442 // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e)
443 // X=(Y-f)/c | else
444 case -5:
445
446 if (R >= pow(Params[1] * Params[4], Params[0]) + Params[5]) {
447
448 Val = pow(R - Params[5], 1/Params[0]) - Params[2] / Params[1];
449 }
450 else {
451 Val = (R - Params[6]) / Params[3];
452 }
453 break;
454
455 default:
456 cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported parametric curve type=%d", abs(Type)-1);
457 cmsFreeGamma(Table);
458 return NULL;
459 }
460
461
462 // Saturate
463
464 dval = Val * 65535.0 + .5;
465 if (dval > 65535.) dval = 65535.0;
466 if (dval < 0) dval = 0;
467
468 Table->GammaTable[i] = (WORD) floor(dval);
469 }
470
471 Table -> Seed.Crc32 = _cmsCrc32OfGammaTable(Table);
472
473 return Table;
474}
475
476// Build a gamma table based on gamma constant
477
478LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma)
479{
480 return cmsBuildParametricGamma(nEntries, 1, &Gamma);
481}
482
483
484
485// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite
486// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press.
487//
488// Smoothing and interpolation with second differences.
489//
490// Input: weights (w), data (y): vector from 1 to m.
491// Input: smoothing parameter (lambda), length (m).
492// Output: smoothed vector (z): vector from 1 to m.
493
494
495static
496void smooth2(vec w, vec y, vec z, float lambda, int m)
497{
498 int i, i1, i2;
499 vec c, d, e;
500 d[1] = w[1] + lambda;
501 c[1] = -2 * lambda / d[1];
502 e[1] = lambda /d[1];
503 z[1] = w[1] * y[1];
504 d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1];
505 c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2];
506 e[2] = lambda / d[2];
507 z[2] = w[2] * y[2] - c[1] * z[1];
508 for (i = 3; i < m - 1; i++) {
509 i1 = i - 1; i2 = i - 2;
510 d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
511 c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i];
512 e[i] = lambda / d[i];
513 z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2];
514 }
515 i1 = m - 2; i2 = m - 3;
516 d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
517 c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1];
518 z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2];
519 i1 = m - 1; i2 = m - 2;
520 d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
521 z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m];
522 z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m];
523 for (i = m - 2; 1<= i; i--)
524 z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2];
525}
526
527
528
529// Smooths a curve sampled at regular intervals
530
531BOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda)
532
533{
534 vec w, y, z;
535 int i, nItems, Zeros, Poles;
536
537
538 if (cmsIsLinear(Tab->GammaTable, Tab->nEntries)) return FALSE; // Nothing to do
539
540 nItems = Tab -> nEntries;
541
542 if (nItems > MAX_KNOTS) {
543 cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothGamma: too many points.");
544 return FALSE;
545 }
546
547 ZeroMemory(w, nItems * sizeof(float));
548 ZeroMemory(y, nItems * sizeof(float));
549 ZeroMemory(z, nItems * sizeof(float));
550
551 for (i=0; i < nItems; i++)
552 {
553 y[i+1] = (float) Tab -> GammaTable[i];
554 w[i+1] = 1.0;
555 }
556
557 smooth2(w, y, z, (float) lambda, nItems);
558
559 // Do some reality - checking...
560 Zeros = Poles = 0;
561 for (i=nItems; i > 1; --i) {
562
563 if (z[i] == 0.) Zeros++;
564 if (z[i] >= 65535.) Poles++;
565 if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
566 }
567
568 if (Zeros > (nItems / 3)) return FALSE; // Degenerated, mostly zeros
569 if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles
570
571 // Seems ok
572
573 for (i=0; i < nItems; i++) {
574
575 // Clamp to WORD
576
577 float v = z[i+1];
578
579 if (v < 0) v = 0;
580 if (v > 65535.) v = 65535.;
581
582 Tab -> GammaTable[i] = (WORD) floor(v + .5);
583 }
584
585 return TRUE;
586}
587
588
589// Check if curve is exponential, return gamma if so.
590
591double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold)
592{
593 double gamma, sum, sum2;
594 double n, x, y, Std;
595 int i;
596
597 sum = sum2 = n = 0;
598
599 // Does exclude endpoints
600 for (i=1; i < nEntries - 1; i++) {
601
602 x = (double) i / (nEntries - 1);
603 y = (double) GammaTable[i] / 65535.;
604
605 // Avoid 7% on lower part to prevent
606 // artifacts due to linear ramps
607
608 if (y > 0. && y < 1. && x > 0.07) {
609
610 gamma = log(y) / log(x);
611 sum += gamma;
612 sum2 += gamma * gamma;
613 n++;
614 }
615
616 }
617
618 // Take a look on SD to see if gamma isn't exponential at all
619 Std = sqrt((n * sum2 - sum * sum) / (n*(n-1)));
620
621
622 if (Std > Thereshold)
623 return -1.0;
624
625 return (sum / n); // The mean
626}
627
628
629double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t)
630{
631 return cmsEstimateGammaEx(t->GammaTable, t->nEntries, 0.7);
632}
633
634
635// -----------------------------------------------------------------Sampled curves
636
637// Allocate a empty curve
638
639LPSAMPLEDCURVE cmsAllocSampledCurve(int nItems)
640{
641 LPSAMPLEDCURVE pOut;
642
643 pOut = (LPSAMPLEDCURVE) malloc(sizeof(SAMPLEDCURVE));
644 if (pOut == NULL)
645 return NULL;
646
647 if((pOut->Values = (double *) malloc(nItems * sizeof(double))) == NULL)
648 {
649 free(pOut);
650 return NULL;
651 }
652
653 pOut->nItems = nItems;
654 ZeroMemory(pOut->Values, nItems * sizeof(double));
655
656 return pOut;
657}
658
659
660void cmsFreeSampledCurve(LPSAMPLEDCURVE p)
661{
662 free((LPVOID) p -> Values);
663 free((LPVOID) p);
664}
665
666
667
668// Does duplicate a sampled curve
669
670LPSAMPLEDCURVE cmsDupSampledCurve(LPSAMPLEDCURVE p)
671{
672 LPSAMPLEDCURVE out;
673
674 out = cmsAllocSampledCurve(p -> nItems);
675 if (!out) return NULL;
676
677 CopyMemory(out ->Values, p ->Values, p->nItems * sizeof(double));
678
679 return out;
680}
681
682
683// Take min, max of curve
684
685void cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max)
686{
687 int i;
688
689 *Min = 65536.;
690 *Max = 0.;
691
692 for (i=0; i < p -> nItems; i++) {
693
694 double v = p -> Values[i];
695
696 if (v < *Min)
697 *Min = v;
698
699 if (v > *Max)
700 *Max = v;
701 }
702
703 if (*Min < 0) *Min = 0;
704 if (*Max > 65535.0) *Max = 65535.0;
705}
706
707// Clamps to Min, Max
708
709void cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max)
710{
711
712 int i;
713
714 for (i=0; i < p -> nItems; i++) {
715
716 double v = p -> Values[i];
717
718 if (v < Min)
719 v = Min;
720
721 if (v > Max)
722 v = Max;
723
724 p -> Values[i] = v;
725
726 }
727
728}
729
730
731
732// Smooths a curve sampled at regular intervals
733
734BOOL cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double lambda)
735{
736 vec w, y, z;
737 int i, nItems;
738
739 nItems = Tab -> nItems;
740
741 if (nItems > MAX_KNOTS) {
742 cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothSampledCurve: too many points.");
743 return FALSE;
744 }
745
746 ZeroMemory(w, nItems * sizeof(float));
747 ZeroMemory(y, nItems * sizeof(float));
748 ZeroMemory(z, nItems * sizeof(float));
749
750 for (i=0; i < nItems; i++)
751 {
752 float value = (float) Tab -> Values[i];
753
754 y[i+1] = value;
755 w[i+1] = (float) ((value < 0.0) ? 0 : 1);
756 }
757
758
759 smooth2(w, y, z, (float) lambda, nItems);
760
761 for (i=0; i < nItems; i++) {
762
763 Tab -> Values[i] = z[i+1];;
764 }
765
766 return TRUE;
767
768}
769
770
771// Scale a value v, within domain Min .. Max
772// to a domain 0..(nPoints-1)
773
774static
775double ScaleVal(double v, double Min, double Max, int nPoints)
776{
777
778 double a, b;
779
780 if (v <= Min) return 0;
781 if (v >= Max) return (nPoints-1);
782
783 a = (double) (nPoints - 1) / (Max - Min);
784 b = a * Min;
785
786 return (a * v) - b;
787
788}
789
790
791// Does rescale a sampled curve to fit in a 0..(nPoints-1) domain
792
793void cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints)
794{
795
796 int i;
797
798 for (i=0; i < p -> nItems; i++) {
799
800 double v = p -> Values[i];
801
802 p -> Values[i] = ScaleVal(v, Min, Max, nPoints);
803 }
804
805}
806
807
808// Joins two sampled curves for X and Y. Curves should be sorted.
809
810LPSAMPLEDCURVE cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints)
811{
812 int i, j;
813 LPSAMPLEDCURVE out;
814 double MinX, MinY, MaxX, MaxY;
815 double x, y, x1, y1, x2, y2, a, b;
816
817 out = cmsAllocSampledCurve(nResultingPoints);
818 if (out == NULL)
819 return NULL;
820
821 if (X -> nItems != Y -> nItems) {
822
823 cmsSignalError(LCMS_ERRC_ABORTED, "cmsJoinSampledCurves: invalid curve.");
824 cmsFreeSampledCurve(out);
825 return NULL;
826 }
827
828 // Get endpoints of sampled curves
829 cmsEndpointsOfSampledCurve(X, &MinX, &MaxX);
830 cmsEndpointsOfSampledCurve(Y, &MinY, &MaxY);
831
832
833 // Set our points
834 out ->Values[0] = MinY;
835 for (i=1; i < nResultingPoints; i++) {
836
837 // Scale t to x domain
838 x = (i * (MaxX - MinX) / (nResultingPoints-1)) + MinX;
839
840 // Find interval in which j is within (always up,
841 // since fn should be monotonic at all)
842
843 j = 1;
844 while ((j < X ->nItems - 1) && X ->Values[j] < x)
845 j++;
846
847 // Now x is within X[j-1], X[j]
848 x1 = X ->Values[j-1]; x2 = X ->Values[j];
849 y1 = Y ->Values[j-1]; y2 = Y ->Values[j];
850
851 // Interpolate the value
852 a = (y1 - y2) / (x1 - x2);
853 b = y1 - a * x1;
854 y = a* x + b;
855
856 out ->Values[i] = y;
857 }
858
859
860 cmsClampSampledCurve(out, MinY, MaxY);
861 return out;
862}
863
864
865
866// Convert between curve types
867
868LPGAMMATABLE cmsConvertSampledCurveToGamma(LPSAMPLEDCURVE Sampled, double Max)
869{
870 LPGAMMATABLE Gamma;
871 int i, nPoints;
872
873
874 nPoints = Sampled ->nItems;
875
876 Gamma = cmsAllocGamma(nPoints);
877 for (i=0; i < nPoints; i++) {
878
879 Gamma->GammaTable[i] = (WORD) floor(ScaleVal(Sampled ->Values[i], 0, Max, 65536) + .5);
880 }
881
882 return Gamma;
883
884}
885
886// Inverse of anterior
887
888LPSAMPLEDCURVE cmsConvertGammaToSampledCurve(LPGAMMATABLE Gamma, int nPoints)
889{
890 LPSAMPLEDCURVE Sampled;
891 L16PARAMS L16;
892 int i;
893 WORD wQuant, wValIn;
894
895 if (nPoints > 4096) {
896
897 cmsSignalError(LCMS_ERRC_ABORTED, "cmsConvertGammaToSampledCurve: too many points (max=4096)");
898 return NULL;
899 }
900
901 cmsCalcL16Params(Gamma -> nEntries, &L16);
902
903 Sampled = cmsAllocSampledCurve(nPoints);
904 for (i=0; i < nPoints; i++) {
905 wQuant = _cmsQuantizeVal(i, nPoints);
906 wValIn = cmsLinearInterpLUT16(wQuant, Gamma ->GammaTable, &L16);
907 Sampled ->Values[i] = (float) wValIn;
908 }
909
910 return Sampled;
911}
912
913
914
915
916// Smooth endpoints (used in Black/White compensation)
917
918BOOL _cmsSmoothEndpoints(LPWORD Table, int nEntries)
919{
920 vec w, y, z;
921 int i, Zeros, Poles;
922
923#ifdef DEBUG
924 ASAVE(Table, nEntries, "nonsmt.txt");
925#endif
926
927
928 if (cmsIsLinear(Table, nEntries)) return FALSE; // Nothing to do
929
930
931 if (nEntries > MAX_KNOTS) {
932 cmsSignalError(LCMS_ERRC_ABORTED, "_cmsSmoothEndpoints: too many points.");
933 return FALSE;
934 }
935
936 ZeroMemory(w, nEntries * sizeof(float));
937 ZeroMemory(y, nEntries * sizeof(float));
938 ZeroMemory(z, nEntries * sizeof(float));
939
940 for (i=0; i < nEntries; i++)
941 {
942 y[i+1] = (float) Table[i];
943 w[i+1] = 1.0;
944 }
945
946 w[1] = 65535.0;
947 w[nEntries] = 65535.0;
948
949 smooth2(w, y, z, (float) nEntries, nEntries);
950
951 // Do some reality - checking...
952 Zeros = Poles = 0;
953 for (i=nEntries; i > 1; --i) {
954
955 if (z[i] == 0.) Zeros++;
956 if (z[i] >= 65535.) Poles++;
957 if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
958 }
959
960 if (Zeros > (nEntries / 3)) return FALSE; // Degenerated, mostly zeros
961 if (Poles > (nEntries / 3)) return FALSE; // Degenerated, mostly poles
962
963 // Seems ok
964
965 for (i=0; i < nEntries; i++) {
966
967 // Clamp to WORD
968
969 float v = z[i+1];
970
971 if (v < 0) v = 0;
972 if (v > 65535.) v = 65535.;
973
974 Table[i] = (WORD) floor(v + .5);
975 }
976
977 return TRUE;
978}