2
/* Little cms - profiler construction set */
3
/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
5
/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
6
/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
7
/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
9
/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
10
/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
11
/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
12
/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
13
/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
14
/* OF THIS SOFTWARE. */
16
/* This file is free software; you can redistribute it and/or modify it */
17
/* under the terms of the GNU General Public License as published by */
18
/* the Free Software Foundation; either version 2 of the License, or */
19
/* (at your option) any later version. */
21
/* This program is distributed in the hope that it will be useful, but */
22
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
23
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
24
/* General Public License for more details. */
26
/* You should have received a copy of the GNU General Public License */
27
/* along with this program; if not, write to the Free Software */
28
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. */
30
/* As a special exception to the GNU General Public License, if you */
31
/* distribute this file as part of a program that contains a */
32
/* configuration script generated by Autoconf, you may include it under */
33
/* the same distribution terms that you use for the rest of that program. */
41
/* The scanner profiler */
44
BOOL cdecl cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys);
45
BOOL cdecl cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys);
47
/* ------------------------------------------------------------ Implementation */
51
/* Does create regression matrix */
54
void ComputeGlobalRegression(LPSCANNERPROFILERDATA sys)
60
nTerms = cmsxFindOptimumNumOfTerms(&sys ->hdr, 55, &lAllOk);
64
if (sys -> hdr.printf)
65
sys -> hdr.printf("*** WARNING: Inconsistence found, profile may be wrong. Check the target!");
69
/* Create high terms matrix used by interpolation */
70
cmsxRegressionCreateMatrix(&sys -> hdr.m,
77
if (sys -> hdr.printf)
78
sys -> hdr.printf("Global regression: %d terms, R2Adj = %g", nTerms, Stat.R2adj);
80
/* Create low terms matrix used by extrapolation */
81
cmsxRegressionCreateMatrix(&sys -> hdr.m,
83
(nTerms > 10 ? 10 : nTerms),
87
if (sys -> hdr.printf)
88
sys -> hdr.printf("Extrapolation: R2Adj = %g", Stat.R2adj);
93
/* Fill struct with default values */
95
BOOL cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys)
99
if (sys == NULL) return false;
100
ZeroMemory(sys, sizeof(SCANNERPROFILERDATA));
102
sys->hdr.DeviceClass = icSigInputClass;
103
sys->hdr.ColorSpace = icSigRgbData;
104
sys->hdr.PCSType = PT_Lab;
105
sys->hdr.Medium = MEDIUM_REFLECTIVE_D50;
107
/* Default values for generation */
109
sys -> hdr.lUseCIECAM97s = false;
110
sys -> hdr.CLUTPoints = 16;
113
/* Default viewing conditions for scanner */
115
sys -> hdr.device.Yb = 20;
116
sys -> hdr.device.La = 20;
117
sys -> hdr.device.surround = AVG_SURROUND;
118
sys -> hdr.device.D_value = 1.0; /* Complete adaptation */
121
/* Viewing conditions of PCS */
122
cmsxInitPCSViewingConditions(&sys -> hdr);
125
sys -> HiTerms = NULL;
126
sys -> LoTerms = NULL;
128
strcpy(sys -> hdr.Description, "no description");
129
strcpy(sys -> hdr.Manufacturer, "little cms profiler construction set");
130
strcpy(sys -> hdr.Copyright, "No copyright, use freely");
131
strcpy(sys -> hdr.Model, "(unknown)");
133
sys ->lLocalConvergenceExtrapolation = false;
134
sys ->hdr.ProfileVerbosityLevel = 0;
140
/* Auxiliar: take RGB and update gauge */
142
void GetRGB(LPPROFILERCOMMONDATA hdr, register WORD In[], double* r, double* g, double* b)
144
static int Count = 0, n_old = 0;
149
R = _cmsxSaturate65535To255(In[0]); /* Convert from the sheet notation */
150
G = _cmsxSaturate65535To255(In[1]); /* 0..255.0, to our notation */
151
B = _cmsxSaturate65535To255(In[2]); /* of 0..0xffff, 0xffff/255 = 257 */
153
if (R == 0 && G == 0 && B == 0) {
154
Count = 0; n_old = -1;
157
n = (int) (double) (100. * Count) / (hdr->CLUTPoints * hdr->CLUTPoints * hdr->CLUTPoints);
161
if (hdr->Gauger) hdr->Gauger("", 0, 100, (int) n);
165
*r = R; *g = G; *b = B;
173
/* The sampler for Lab */
175
int RegressionSamplerLab(register WORD In[], register WORD Out[], register LPVOID Cargo)
180
LPSCANNERPROFILERDATA sys = (LPSCANNERPROFILERDATA) Cargo;
184
GetRGB(&sys->hdr, In, &r, &g, &b);
187
code = cmsxHullCheckpoint(sys->hdr.hRGBHull,
190
(int) floor(b + .5));
193
if (code == 'i') { /* Inside gamut */
195
if (!cmsxRegressionRGB2Lab(r, g, b, sys -> HiTerms, &Lab)) return false;
198
if (!sys -> lLocalConvergenceExtrapolation && code == 'o') { /* outside gamut */
200
if (!cmsxRegressionRGB2Lab(r, g, b, sys -> LoTerms, &Lab)) return false;
202
else { /* At gamut hull boundaries */
204
if (!cmsxRegressionInterpolatorRGB(&sys -> hdr.m,
214
/* Regression CAN deliver wrong values. Clamp these. */
215
cmsClampLab(&Lab, 127.9961, -128, 127.9961, -128);
218
cmsLab2XYZ(cmsD50_XYZ(), &xyz, &Lab);
219
cmsxChromaticAdaptationAndNormalization(&sys->hdr, &xyz, false);
220
cmsXYZ2Lab(cmsD50_XYZ(), &Lab, &xyz);
222
/* Clamping again, adaptation could move slightly values */
223
cmsClampLab(&Lab, 127.9961, -128, 127.9961, -128);
225
/* To PCS encoding */
226
cmsFloat2LabEncoded(Out, &Lab);
229
return true; /* And done with success */
236
/* The sampler for XYZ */
238
int RegressionSamplerXYZ(register WORD In[], register WORD Out[], register LPVOID Cargo)
242
LPSCANNERPROFILERDATA sys = (LPSCANNERPROFILERDATA) Cargo;
245
GetRGB(&sys -> hdr, In, &r, &g, &b);
247
code = cmsxHullCheckpoint(sys ->hdr.hRGBHull,
250
(int) floor(b + .5));
252
if (code == 'i') { /* Inside gamut */
254
if (!cmsxRegressionRGB2XYZ(r, g, b, sys -> HiTerms, &xyz)) return false;
257
if (!sys -> lLocalConvergenceExtrapolation && code == 'o') { /* outside gamut */
259
if (!cmsxRegressionRGB2XYZ(r, g, b, sys -> LoTerms, &xyz)) return false;
262
else { /* At gamut hull boundaries */
264
if (!cmsxRegressionInterpolatorRGB(&sys -> hdr.m,
278
cmsxChromaticAdaptationAndNormalization(&sys->hdr, &xyz, false);
280
/* To PCS encoding. It also claps bad values */
281
cmsFloat2XYZEncoded(Out, &xyz);
283
return true; /* And done witch success */
288
/* The main scanner profiler */
289
BOOL cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys)
296
if (!*sys -> hdr.OutputProfileFile)
300
if (!cmsxChoosePCS(&sys->hdr))
303
dwNeedSamples = PATCH_HAS_RGB;
304
if (sys ->hdr.PCSType == PT_Lab)
305
dwNeedSamples |= PATCH_HAS_Lab;
307
dwNeedSamples |= PATCH_HAS_XYZ;
310
if (sys->hdr.printf) {
312
sys->hdr.printf("Loading sheets...");
314
if (sys->hdr.ReferenceSheet[0])
315
sys->hdr.printf("Reference sheet: %s", sys->hdr.ReferenceSheet);
316
if (sys->hdr.MeasurementSheet[0])
317
sys->hdr.printf("Measurement sheet: %s", sys->hdr.MeasurementSheet);
321
if (!cmsxPCollBuildMeasurement(&sys->hdr.m,
322
sys->hdr.ReferenceSheet,
323
sys->hdr.MeasurementSheet,
324
dwNeedSamples)) return false;
328
sys->hdr.hProfile = cmsCreateRGBProfile(NULL, NULL, NULL);
331
cmsSetDeviceClass(sys->hdr.hProfile, sys->hdr.DeviceClass);
332
cmsSetColorSpace(sys->hdr.hProfile, sys->hdr.ColorSpace);
333
cmsSetPCS(sys->hdr. hProfile, _cmsICCcolorSpace(sys->hdr.PCSType));
335
/* Save char target tag */
336
if (sys->hdr.ProfileVerbosityLevel >= 2) {
338
cmsxEmbedCharTarget(&sys ->hdr);
341
AToB0 = cmsAllocLUT();
343
cmsAlloc3DGrid(AToB0, sys->hdr.CLUTPoints, 3, 3);
345
cmsxComputeLinearizationTables(&sys-> hdr.m,
347
sys -> Prelinearization,
349
MEDIUM_REFLECTIVE_D50);
351
/* Refresh RGB of all patches. This converts all regression into */
352
/* near linear RGB->Lab or XYZ */
354
cmsxPCollLinearizePatches(&sys->hdr.m, sys -> hdr.m.Allowed, sys -> Prelinearization);
356
cmsxComputeGamutHull(&sys->hdr);
357
ComputeGlobalRegression(sys);
359
cmsAllocLinearTable(AToB0, sys -> Prelinearization, 1);
361
/* Set CIECAM97s parameters */
363
sys -> hdr.device.whitePoint.X = sys -> hdr.WhitePoint.X * 100.;
364
sys -> hdr.device.whitePoint.Y = sys -> hdr.WhitePoint.Y * 100.;
365
sys -> hdr.device.whitePoint.Z = sys -> hdr.WhitePoint.Z * 100.;
368
sys->hdr.hDevice = cmsCIECAM97sInit(&sys->hdr.device);
369
sys->hdr.hPCS = cmsCIECAM97sInit(&sys->hdr.PCS);
372
if (sys -> hdr.PCSType == PT_Lab)
373
cmsSample3DGrid(AToB0, RegressionSamplerLab, sys, 0);
375
cmsSample3DGrid(AToB0, RegressionSamplerXYZ, sys, 0);
377
cmsCIECAM97sDone(sys->hdr.hDevice);
378
cmsCIECAM97sDone(sys->hdr.hPCS);
380
cmsAddTag(sys->hdr.hProfile, icSigAToB0Tag, AToB0);
383
cmsxEmbedTextualInfo(&sys -> hdr);
385
cmsAddTag(sys->hdr.hProfile, icSigMediaWhitePointTag, &sys->hdr.WhitePoint);
386
cmsAddTag(sys->hdr.hProfile, icSigMediaBlackPointTag, &sys->hdr.BlackPoint);
389
/* Save primaries & gamma curves */
390
if (sys->hdr.ProfileVerbosityLevel >= 1) {
392
cmsxEmbedMatrixShaper(&sys ->hdr);
395
_cmsSaveProfile(sys->hdr.hProfile, sys->hdr.OutputProfileFile);
397
cmsCloseProfile(sys->hdr.hProfile);
398
sys->hdr.hProfile = NULL;
400
cmsxPCollFreeMeasurements(&sys->hdr.m);
405
MATNfree(sys -> HiTerms);
406
sys -> HiTerms = NULL;
410
MATNfree(sys -> LoTerms);
411
sys -> LoTerms = NULL;
415
if (sys ->Prelinearization[0])
416
cmsFreeGammaTriple(sys -> Prelinearization);
419
cmsFreeGammaTriple(sys->hdr.Gamma);