Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2454 | craig | 1 | // |
2 | // Little cms - profiler construction set |
||
3 | // Copyright (C) 1998-2001 Marti Maria |
||
4 | // |
||
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. |
||
8 | // |
||
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. |
||
15 | // |
||
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. |
||
20 | // |
||
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. |
||
25 | // |
||
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
||
29 | // |
||
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. |
||
34 | // |
||
35 | // Version 1.08a |
||
36 | |||
37 | |||
38 | #include "lcmsprf.h" |
||
39 | |||
40 | |||
41 | BOOL cdecl cmsxComputeMatrixShaper(const char* ReferenceSheet, |
||
42 | const char* MeasurementSheet, |
||
43 | int Medium, |
||
44 | LPGAMMATABLE TransferCurves[3], |
||
45 | LPcmsCIEXYZ WhitePoint, |
||
46 | LPcmsCIEXYZ BlackPoint, |
||
47 | LPcmsCIExyYTRIPLE Primaries); |
||
48 | |||
49 | |||
50 | |||
51 | // ------------------------------------------------------------- Implementation |
||
52 | |||
53 | |||
54 | static |
||
55 | void Div100(LPcmsCIEXYZ xyz) |
||
56 | { |
||
57 | xyz -> X /= 100; xyz -> Y /= 100; xyz -> Z /= 100; |
||
58 | } |
||
59 | |||
60 | |||
61 | |||
62 | // Compute endpoints |
||
63 | |||
64 | static |
||
65 | BOOL ComputeWhiteAndBlackPoints(LPMEASUREMENT Linearized, |
||
66 | LPGAMMATABLE TransferCurves[3], |
||
67 | LPcmsCIEXYZ Black, LPcmsCIEXYZ White) |
||
68 | { |
||
69 | |||
70 | double Zeroes[3], Ones[3], lmin[3], lmax[3]; |
||
71 | |||
72 | SETOFPATCHES Neutrals = cmsxPCollBuildSet(Linearized, FALSE); |
||
73 | |||
74 | cmsxPCollPatchesNearNeutral(Linearized, Linearized->Allowed, |
||
75 | 15, Neutrals); |
||
76 | |||
77 | Zeroes[0] = Zeroes[1] = Zeroes[2] = 0.0; |
||
78 | Ones[0] = Ones[1] = Ones[2] = 255.0; |
||
79 | |||
80 | |||
81 | cmsxApplyLinearizationTable(Zeroes, TransferCurves, lmin); |
||
82 | cmsxApplyLinearizationTable(Ones, TransferCurves, lmax); |
||
83 | |||
84 | |||
85 | // Global regression to find White & Black points |
||
86 | if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ, |
||
87 | 4, |
||
88 | TRUE, |
||
89 | 12, |
||
90 | lmin[0], lmin[1], lmin[2], |
||
91 | Black)) return FALSE; |
||
92 | |||
93 | if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ, |
||
94 | 4, |
||
95 | TRUE, |
||
96 | 12, |
||
97 | lmax[0], lmax[1], lmax[2], |
||
98 | White)) return FALSE; |
||
99 | |||
100 | _cmsxClampXYZ100(White); |
||
101 | _cmsxClampXYZ100(Black); |
||
102 | |||
103 | return TRUE; |
||
104 | |||
105 | } |
||
106 | |||
107 | |||
108 | // Study convergence of primary axis |
||
109 | |||
110 | static |
||
111 | BOOL ComputePrimary(LPMEASUREMENT Linearized, |
||
112 | LPGAMMATABLE TransferCurves[3], |
||
113 | int n, |
||
114 | LPcmsCIExyY Primary) |
||
115 | { |
||
116 | |||
117 | double Ones[3], lmax[3]; |
||
118 | cmsCIEXYZ PrimXYZ; |
||
119 | SETOFPATCHES SetPrimary; |
||
120 | int nR; |
||
121 | |||
122 | |||
123 | // At first, try to see if primaries are already in measurement |
||
124 | |||
125 | SetPrimary = cmsxPCollBuildSet(Linearized, FALSE); |
||
126 | nR = cmsxPCollPatchesNearPrimary(Linearized, Linearized->Allowed, |
||
127 | n, 32, SetPrimary); |
||
128 | |||
129 | Ones[0] = Ones[1] = Ones[2] = 0; |
||
130 | Ones[n] = 255.0; |
||
131 | |||
132 | cmsxApplyLinearizationTable(Ones, TransferCurves, lmax); |
||
133 | |||
134 | // Do incremental regression to find primaries |
||
135 | if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ, |
||
136 | 4, |
||
137 | FALSE, |
||
138 | 12, |
||
139 | lmax[0], lmax[1], lmax[2], |
||
140 | &PrimXYZ)) return FALSE; |
||
141 | |||
142 | _cmsxClampXYZ100(&PrimXYZ); |
||
143 | cmsXYZ2xyY(Primary, &PrimXYZ); |
||
144 | return TRUE; |
||
145 | |||
146 | |||
147 | } |
||
148 | |||
149 | |||
150 | |||
151 | // Does compute a matrix-shaper based on patches. |
||
152 | |||
153 | static |
||
154 | double Clip(double d) |
||
155 | { |
||
156 | return d > 0 ? d: 0; |
||
157 | } |
||
158 | |||
159 | |||
160 | BOOL cmsxComputeMatrixShaper(const char* ReferenceSheet, |
||
161 | const char* MeasurementSheet, |
||
162 | int Medium, |
||
163 | LPGAMMATABLE TransferCurves[3], |
||
164 | LPcmsCIEXYZ WhitePoint, |
||
165 | LPcmsCIEXYZ BlackPoint, |
||
166 | LPcmsCIExyYTRIPLE Primaries) |
||
167 | { |
||
168 | |||
169 | MEASUREMENT Linearized; |
||
170 | cmsCIEXYZ Black, White; |
||
171 | cmsCIExyYTRIPLE PrimarySet; |
||
172 | LPPATCH PatchWhite, PatchBlack; |
||
173 | LPPATCH PatchRed, PatchGreen, PatchBlue; |
||
174 | double Distance; |
||
175 | |||
176 | // Load sheets |
||
177 | |||
178 | if (!cmsxPCollBuildMeasurement(&Linearized, |
||
179 | ReferenceSheet, |
||
180 | MeasurementSheet, |
||
181 | PATCH_HAS_XYZ|PATCH_HAS_RGB)) return FALSE; |
||
182 | |||
183 | |||
184 | |||
185 | // Any patch to deal of? |
||
186 | if (cmsxPCollCountSet(&Linearized, Linearized.Allowed) <= 0) return FALSE; |
||
187 | |||
188 | |||
189 | // Try to see if proper primaries, white and black already present |
||
190 | PatchWhite = cmsxPCollFindWhite(&Linearized, Linearized.Allowed, &Distance); |
||
191 | if (Distance != 0) |
||
192 | PatchWhite = NULL; |
||
193 | |||
194 | PatchBlack = cmsxPCollFindBlack(&Linearized, Linearized.Allowed, &Distance); |
||
195 | if (Distance != 0) |
||
196 | PatchBlack = NULL; |
||
197 | |||
198 | PatchRed = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 0, &Distance); |
||
199 | if (Distance != 0) |
||
200 | PatchRed = NULL; |
||
201 | |||
202 | PatchGreen = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 1, &Distance); |
||
203 | if (Distance != 0) |
||
204 | PatchGreen = NULL; |
||
205 | |||
206 | PatchBlue = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 2, &Distance); |
||
207 | if (Distance != 0) |
||
208 | PatchBlue= NULL; |
||
209 | |||
210 | // If we got primaries, then we can also get prelinearization |
||
211 | // by Levenberg-Marquardt. This applies on monitor profiles |
||
212 | |||
213 | if (PatchWhite && PatchRed && PatchGreen && PatchBlue) { |
||
214 | |||
215 | // Build matrix with primaries |
||
216 | |||
217 | MAT3 Mat, MatInv; |
||
218 | LPSAMPLEDCURVE Xr,Yr, Xg, Yg, Xb, Yb; |
||
219 | int i, nRes, cnt; |
||
220 | |||
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); |
||
224 | |||
225 | // Invert matrix |
||
226 | MAT3inverse(&Mat, &MatInv); |
||
227 | |||
228 | nRes = cmsxPCollCountSet(&Linearized, Linearized.Allowed); |
||
229 | |||
230 | Xr = cmsAllocSampledCurve(nRes); |
||
231 | Yr = cmsAllocSampledCurve(nRes); |
||
232 | Xg = cmsAllocSampledCurve(nRes); |
||
233 | Yg = cmsAllocSampledCurve(nRes); |
||
234 | Xb = cmsAllocSampledCurve(nRes); |
||
235 | Yb = cmsAllocSampledCurve(nRes); |
||
236 | |||
237 | // Convert XYZ of all patches to RGB |
||
238 | cnt = 0; |
||
239 | for (i=0; i < Linearized.nPatches; i++) { |
||
240 | |||
241 | if (Linearized.Allowed[i]) { |
||
242 | |||
243 | VEC3 RGBprime, XYZ; |
||
244 | LPPATCH p; |
||
245 | |||
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; |
||
250 | |||
251 | MAT3eval(&RGBprime, &MatInv, &XYZ); |
||
252 | |||
253 | Xr ->Values[cnt] = p ->Colorant.RGB[0]; |
||
254 | Yr ->Values[cnt] = Clip(RGBprime.n[0]); |
||
255 | |||
256 | Xg ->Values[cnt] = p ->Colorant.RGB[1]; |
||
257 | Yg ->Values[cnt] = Clip(RGBprime.n[1]); |
||
258 | |||
259 | Xb ->Values[cnt] = p ->Colorant.RGB[2]; |
||
260 | Yb ->Values[cnt] = Clip(RGBprime.n[2]); |
||
261 | |||
262 | cnt++; |
||
263 | |||
264 | } |
||
265 | } |
||
266 | |||
267 | TransferCurves[0] = cmsxEstimateGamma(Xr, Yr, 1024); |
||
268 | TransferCurves[1] = cmsxEstimateGamma(Xg, Yg, 1024); |
||
269 | TransferCurves[2] = cmsxEstimateGamma(Xb, Yb, 1024); |
||
270 | |||
271 | if (WhitePoint) { |
||
272 | |||
273 | WhitePoint->X = PatchWhite->XYZ.X; |
||
274 | WhitePoint->Y= PatchWhite ->XYZ.Y; |
||
275 | WhitePoint->Z= PatchWhite ->XYZ.Z; |
||
276 | } |
||
277 | |||
278 | if (BlackPoint && PatchBlack) { |
||
279 | |||
280 | BlackPoint->X = PatchBlack ->XYZ.X; |
||
281 | BlackPoint->Y = PatchBlack ->XYZ.Y; |
||
282 | BlackPoint->Z = PatchBlack ->XYZ.Z; |
||
283 | } |
||
284 | |||
285 | if (Primaries) { |
||
286 | |||
287 | cmsXYZ2xyY(&Primaries->Red, &PatchRed ->XYZ); |
||
288 | cmsXYZ2xyY(&Primaries->Green, &PatchGreen ->XYZ); |
||
289 | cmsXYZ2xyY(&Primaries->Blue, &PatchBlue ->XYZ); |
||
290 | |||
291 | } |
||
292 | |||
293 | |||
294 | cmsFreeSampledCurve(Xr); |
||
295 | cmsFreeSampledCurve(Yr); |
||
296 | cmsFreeSampledCurve(Xg); |
||
297 | cmsFreeSampledCurve(Yg); |
||
298 | cmsFreeSampledCurve(Xb); |
||
299 | cmsFreeSampledCurve(Yb); |
||
300 | |||
301 | cmsxPCollFreeMeasurements(&Linearized); |
||
302 | |||
303 | return TRUE; |
||
304 | } |
||
305 | |||
306 | |||
307 | |||
308 | |||
309 | // Compute prelinearization |
||
310 | cmsxComputeLinearizationTables(&Linearized, PT_XYZ, TransferCurves, 1024, Medium); |
||
311 | |||
312 | // Linearize measurements |
||
313 | cmsxPCollLinearizePatches(&Linearized, Linearized.Allowed, TransferCurves); |
||
314 | |||
315 | |||
316 | // Endpoints |
||
317 | ComputeWhiteAndBlackPoints(&Linearized, TransferCurves, &Black, &White); |
||
318 | |||
319 | // Primaries |
||
320 | ComputePrimary(&Linearized, TransferCurves, 0, &PrimarySet.Red); |
||
321 | ComputePrimary(&Linearized, TransferCurves, 1, &PrimarySet.Green); |
||
322 | ComputePrimary(&Linearized, TransferCurves, 2, &PrimarySet.Blue); |
||
323 | |||
324 | |||
325 | if (BlackPoint) { |
||
326 | *BlackPoint = Black; |
||
327 | Div100(BlackPoint); |
||
328 | } |
||
329 | |||
330 | if (WhitePoint) { |
||
331 | *WhitePoint = White; |
||
332 | Div100(WhitePoint); |
||
333 | } |
||
334 | |||
335 | |||
336 | if (Primaries) { |
||
337 | *Primaries = PrimarySet; |
||
338 | } |
||
339 | |||
340 | cmsxPCollFreeMeasurements(&Linearized); |
||
341 | |||
342 | return TRUE; |
||
343 | } |
||
344 | |||
345 | |||
346 |