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
BOOL cdecl cmsxComputeMatrixShaper(const char* ReferenceSheet,
42
const char* MeasurementSheet,
44
LPGAMMATABLE TransferCurves[3],
45
LPcmsCIEXYZ WhitePoint,
46
LPcmsCIEXYZ BlackPoint,
47
LPcmsCIExyYTRIPLE Primaries);
51
/* ------------------------------------------------------------- Implementation */
55
void Div100(LPcmsCIEXYZ xyz)
57
xyz -> X /= 100; xyz -> Y /= 100; xyz -> Z /= 100;
62
/* Compute endpoints */
65
BOOL ComputeWhiteAndBlackPoints(LPMEASUREMENT Linearized,
66
LPGAMMATABLE TransferCurves[3],
67
LPcmsCIEXYZ Black, LPcmsCIEXYZ White)
70
double Zeroes[3], Ones[3], lmin[3], lmax[3];
72
SETOFPATCHES Neutrals = cmsxPCollBuildSet(Linearized, false);
74
cmsxPCollPatchesNearNeutral(Linearized, Linearized->Allowed,
77
Zeroes[0] = Zeroes[1] = Zeroes[2] = 0.0;
78
Ones[0] = Ones[1] = Ones[2] = 255.0;
81
cmsxApplyLinearizationTable(Zeroes, TransferCurves, lmin);
82
cmsxApplyLinearizationTable(Ones, TransferCurves, lmax);
85
/* Global regression to find White & Black points */
86
if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ,
90
lmin[0], lmin[1], lmin[2],
93
if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ,
97
lmax[0], lmax[1], lmax[2],
100
_cmsxClampXYZ100(White);
101
_cmsxClampXYZ100(Black);
108
/* Study convergence of primary axis */
111
BOOL ComputePrimary(LPMEASUREMENT Linearized,
112
LPGAMMATABLE TransferCurves[3],
117
double Ones[3], lmax[3];
119
SETOFPATCHES SetPrimary;
123
/* At first, try to see if primaries are already in measurement */
125
SetPrimary = cmsxPCollBuildSet(Linearized, false);
126
nR = cmsxPCollPatchesNearPrimary(Linearized, Linearized->Allowed,
129
Ones[0] = Ones[1] = Ones[2] = 0;
132
cmsxApplyLinearizationTable(Ones, TransferCurves, lmax);
134
/* Do incremental regression to find primaries */
135
if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ,
139
lmax[0], lmax[1], lmax[2],
140
&PrimXYZ)) return false;
142
_cmsxClampXYZ100(&PrimXYZ);
143
cmsXYZ2xyY(Primary, &PrimXYZ);
151
/* Does compute a matrix-shaper based on patches. */
154
double Clip(double d)
160
BOOL cmsxComputeMatrixShaper(const char* ReferenceSheet,
161
const char* MeasurementSheet,
163
LPGAMMATABLE TransferCurves[3],
164
LPcmsCIEXYZ WhitePoint,
165
LPcmsCIEXYZ BlackPoint,
166
LPcmsCIExyYTRIPLE Primaries)
169
MEASUREMENT Linearized;
170
cmsCIEXYZ Black, White;
171
cmsCIExyYTRIPLE PrimarySet;
172
LPPATCH PatchWhite, PatchBlack;
173
LPPATCH PatchRed, PatchGreen, PatchBlue;
178
if (!cmsxPCollBuildMeasurement(&Linearized,
181
PATCH_HAS_XYZ|PATCH_HAS_RGB)) return false;
185
/* Any patch to deal of? */
186
if (cmsxPCollCountSet(&Linearized, Linearized.Allowed) <= 0) return false;
189
/* Try to see if proper primaries, white and black already present */
190
PatchWhite = cmsxPCollFindWhite(&Linearized, Linearized.Allowed, &Distance);
194
PatchBlack = cmsxPCollFindBlack(&Linearized, Linearized.Allowed, &Distance);
198
PatchRed = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 0, &Distance);
202
PatchGreen = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 1, &Distance);
206
PatchBlue = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 2, &Distance);
210
/* If we got primaries, then we can also get prelinearization */
211
/* by Levenberg-Marquardt. This applies on monitor profiles */
213
if (PatchWhite && PatchRed && PatchGreen && PatchBlue) {
215
/* Build matrix with primaries */
218
LPSAMPLEDCURVE Xr,Yr, Xg, Yg, Xb, Yb;
221
VEC3init(&Mat.v[0], PatchRed->XYZ.X, PatchGreen->XYZ.X, PatchBlue->XYZ.X);
222
VEC3init(&Mat.v[1], PatchRed->XYZ.Y, PatchGreen->XYZ.Y, PatchBlue->XYZ.Y);
223
VEC3init(&Mat.v[2], PatchRed->XYZ.Z, PatchGreen->XYZ.Z, PatchBlue->XYZ.Z);
226
MAT3inverse(&Mat, &MatInv);
228
nRes = cmsxPCollCountSet(&Linearized, Linearized.Allowed);
230
Xr = cmsAllocSampledCurve(nRes);
231
Yr = cmsAllocSampledCurve(nRes);
232
Xg = cmsAllocSampledCurve(nRes);
233
Yg = cmsAllocSampledCurve(nRes);
234
Xb = cmsAllocSampledCurve(nRes);
235
Yb = cmsAllocSampledCurve(nRes);
237
/* Convert XYZ of all patches to RGB */
239
for (i=0; i < Linearized.nPatches; i++) {
241
if (Linearized.Allowed[i]) {
246
p = Linearized.Patches + i;
247
XYZ.n[0] = p -> XYZ.X;
248
XYZ.n[1] = p -> XYZ.Y;
249
XYZ.n[2] = p -> XYZ.Z;
251
MAT3eval(&RGBprime, &MatInv, &XYZ);
253
Xr ->Values[cnt] = p ->Colorant.RGB[0];
254
Yr ->Values[cnt] = Clip(RGBprime.n[0]);
256
Xg ->Values[cnt] = p ->Colorant.RGB[1];
257
Yg ->Values[cnt] = Clip(RGBprime.n[1]);
259
Xb ->Values[cnt] = p ->Colorant.RGB[2];
260
Yb ->Values[cnt] = Clip(RGBprime.n[2]);
267
TransferCurves[0] = cmsxEstimateGamma(Xr, Yr, 1024);
268
TransferCurves[1] = cmsxEstimateGamma(Xg, Yg, 1024);
269
TransferCurves[2] = cmsxEstimateGamma(Xb, Yb, 1024);
273
WhitePoint->X = PatchWhite->XYZ.X;
274
WhitePoint->Y= PatchWhite ->XYZ.Y;
275
WhitePoint->Z= PatchWhite ->XYZ.Z;
278
if (BlackPoint && PatchBlack) {
280
BlackPoint->X = PatchBlack ->XYZ.X;
281
BlackPoint->Y = PatchBlack ->XYZ.Y;
282
BlackPoint->Z = PatchBlack ->XYZ.Z;
287
cmsXYZ2xyY(&Primaries->Red, &PatchRed ->XYZ);
288
cmsXYZ2xyY(&Primaries->Green, &PatchGreen ->XYZ);
289
cmsXYZ2xyY(&Primaries->Blue, &PatchBlue ->XYZ);
294
cmsFreeSampledCurve(Xr);
295
cmsFreeSampledCurve(Yr);
296
cmsFreeSampledCurve(Xg);
297
cmsFreeSampledCurve(Yg);
298
cmsFreeSampledCurve(Xb);
299
cmsFreeSampledCurve(Yb);
301
cmsxPCollFreeMeasurements(&Linearized);
309
/* Compute prelinearization */
310
cmsxComputeLinearizationTables(&Linearized, PT_XYZ, TransferCurves, 1024, Medium);
312
/* Linearize measurements */
313
cmsxPCollLinearizePatches(&Linearized, Linearized.Allowed, TransferCurves);
317
ComputeWhiteAndBlackPoints(&Linearized, TransferCurves, &Black, &White);
320
ComputePrimary(&Linearized, TransferCurves, 0, &PrimarySet.Red);
321
ComputePrimary(&Linearized, TransferCurves, 1, &PrimarySet.Green);
322
ComputePrimary(&Linearized, TransferCurves, 2, &PrimarySet.Blue);
337
*Primaries = PrimarySet;
340
cmsxPCollFreeMeasurements(&Linearized);