Bug 399703 - Password manager offers to store logins with empty passwords. r=gavin
[wine-gecko.git] / modules / lcms / src / cmssamp.c
blob3ac73b5ea461ff6de9b6326998e80a40f5945849
1 //
2 // Little cms
3 // Copyright (C) 1998-2007 Marti Maria
4 //
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.
24 #include "lcms.h"
27 // ---------------------------------------------------------------------------------
29 static volatile int GlobalBlackPreservationStrategy = 0;
31 // Quantize a value 0 <= i < MaxSamples
33 WORD _cmsQuantizeVal(double i, int MaxSamples)
35 double x;
37 x = ((double) i * 65535.) / (double) (MaxSamples - 1);
39 return (WORD) floor(x + .5);
43 // Is a table linear?
45 int cmsIsLinear(WORD Table[], int nEntries)
47 register int i;
48 int diff;
50 for (i=0; i < nEntries; i++) {
52 diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries));
53 if (diff > 3)
54 return 0;
57 return 1;
62 // pow() restricted to integer
64 static
65 int ipow(int base, int exp)
67 int res = base;
69 while (--exp)
70 res *= base;
72 return res;
76 // Given n, 0<=n<=clut^dim, returns the colorant.
78 static
79 int ComponentOf(int n, int clut, int nColorant)
81 if (nColorant <= 0)
82 return (n % clut);
84 n /= ipow(clut, nColorant);
86 return (n % clut);
91 // This routine does a sweep on whole input space, and calls its callback
92 // function on knots. returns TRUE if all ok, FALSE otherwise.
94 LCMSBOOL LCMSEXPORT cmsSample3DGrid(LPLUT Lut, _cmsSAMPLER Sampler, LPVOID Cargo, DWORD dwFlags)
96 int i, t, nTotalPoints, Colorant, index;
97 WORD In[MAXCHANNELS], Out[MAXCHANNELS];
99 nTotalPoints = ipow(Lut->cLutPoints, Lut -> InputChan);
101 index = 0;
102 for (i = 0; i < nTotalPoints; i++) {
104 for (t=0; t < (int) Lut -> InputChan; t++) {
106 Colorant = ComponentOf(i, Lut -> cLutPoints, (Lut -> InputChan - t - 1 ));
107 In[t] = _cmsQuantizeVal(Colorant, Lut -> cLutPoints);
111 if (dwFlags & SAMPLER_HASTL1) {
113 for (t=0; t < (int) Lut -> InputChan; t++)
114 In[t] = cmsReverseLinearInterpLUT16(In[t],
115 Lut -> L1[t],
116 &Lut -> In16params);
119 for (t=0; t < (int) Lut -> OutputChan; t++)
120 Out[t] = Lut->T[index + t];
122 if (dwFlags & SAMPLER_HASTL2) {
124 for (t=0; t < (int) Lut -> OutputChan; t++)
125 Out[t] = cmsLinearInterpLUT16(Out[t],
126 Lut -> L2[t],
127 &Lut -> Out16params);
131 if (!Sampler(In, Out, Cargo))
132 return FALSE;
134 if (!(dwFlags & SAMPLER_INSPECT)) {
136 if (dwFlags & SAMPLER_HASTL2) {
138 for (t=0; t < (int) Lut -> OutputChan; t++)
139 Out[t] = cmsReverseLinearInterpLUT16(Out[t],
140 Lut -> L2[t],
141 &Lut -> Out16params);
145 for (t=0; t < (int) Lut -> OutputChan; t++)
146 Lut->T[index + t] = Out[t];
150 index += Lut -> OutputChan;
154 return TRUE;
162 // choose reasonable resolution
163 int _cmsReasonableGridpointsByColorspace(icColorSpaceSignature Colorspace, DWORD dwFlags)
165 int nChannels;
167 // Already specified?
168 if (dwFlags & 0x00FF0000) {
169 // Yes, grab'em
170 return (dwFlags >> 16) & 0xFF;
173 nChannels = _cmsChannelsOf(Colorspace);
175 // HighResPrecalc is maximum resolution
177 if (dwFlags & cmsFLAGS_HIGHRESPRECALC) {
179 if (nChannels > 4)
180 return 7; // 7 for Hifi
182 if (nChannels == 4) // 23 for CMYK
183 return 23;
185 return 49; // 49 for RGB and others
189 // LowResPrecal is stripped resolution
191 if (dwFlags & cmsFLAGS_LOWRESPRECALC) {
193 if (nChannels > 4)
194 return 6; // 6 for Hifi
196 if (nChannels == 1)
197 return 33; // For monochrome
199 return 17; // 17 for remaining
202 // Default values
204 if (nChannels > 4)
205 return 7; // 7 for Hifi
207 if (nChannels == 4)
208 return 17; // 17 for CMYK
210 return 33; // 33 for RGB
214 // Sampler implemented by another transform. This is a clean way to
215 // precalculate the devicelink 3D CLUT for almost any transform
217 static
218 int XFormSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
220 cmsDoTransform((cmsHTRANSFORM) Cargo, In, Out, 1);
221 return TRUE;
224 // This routine does compute the devicelink CLUT containing whole
225 // transform. Handles any channel number.
227 LPLUT _cmsPrecalculateDeviceLink(cmsHTRANSFORM h, DWORD dwFlags)
229 _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h;
230 LPLUT Grid;
231 int nGridPoints;
232 DWORD dwFormatIn, dwFormatOut;
233 DWORD SaveFormatIn, SaveFormatOut;
234 int ChannelsIn, ChannelsOut;
235 LPLUT SaveGamutLUT;
238 // Remove any gamut checking
239 SaveGamutLUT = p ->Gamut;
240 p ->Gamut = NULL;
242 ChannelsIn = _cmsChannelsOf(p -> EntryColorSpace);
243 ChannelsOut = _cmsChannelsOf(p -> ExitColorSpace);
245 nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
247 Grid = cmsAllocLUT();
248 if (!Grid) return NULL;
250 Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsIn, ChannelsOut);
252 // Compute device link on 16-bit basis
253 dwFormatIn = (CHANNELS_SH(ChannelsIn)|BYTES_SH(2));
254 dwFormatOut = (CHANNELS_SH(ChannelsOut)|BYTES_SH(2));
256 SaveFormatIn = p ->InputFormat;
257 SaveFormatOut = p ->OutputFormat;
259 p -> InputFormat = dwFormatIn;
260 p -> OutputFormat = dwFormatOut;
261 p -> FromInput = _cmsIdentifyInputFormat(p, dwFormatIn);
262 p -> ToOutput = _cmsIdentifyOutputFormat(p, dwFormatOut);
264 // Fix gamut & gamma possible mismatches.
266 if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION)) {
268 cmsHTRANSFORM hOne[1];
269 hOne[0] = h;
271 _cmsComputePrelinearizationTablesFromXFORM(hOne, 1, Grid);
274 // Attention to this typecast! we can take the luxury to
275 // do this since cmsHTRANSFORM is only an alias to a pointer
276 // to the transform struct.
278 if (!cmsSample3DGrid(Grid, XFormSampler, (LPVOID) p, Grid -> wFlags)) {
280 cmsFreeLUT(Grid);
281 Grid = NULL;
284 p ->Gamut = SaveGamutLUT;
285 p ->InputFormat = SaveFormatIn;
286 p ->OutputFormat = SaveFormatOut;
288 return Grid;
293 // Sampler for Black-preserving CMYK->CMYK transforms
295 typedef struct {
296 cmsHTRANSFORM cmyk2cmyk;
297 cmsHTRANSFORM cmyk2Lab;
298 LPGAMMATABLE KTone;
299 L16PARAMS KToneParams;
300 LPLUT LabK2cmyk;
301 double MaxError;
303 cmsHTRANSFORM hRoundTrip;
304 int MaxTAC;
306 cmsHTRANSFORM hProofOutput;
308 } BPCARGO, *LPBPCARGO;
312 // Preserve black only if that is the only ink used
313 static
314 int BlackPreservingGrayOnlySampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
316 BPCARGO* bp = (LPBPCARGO) Cargo;
318 // If going across black only, keep black only
319 if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
321 // TAC does not apply because it is black ink!
322 Out[0] = Out[1] = Out[2] = 0;
323 Out[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
324 return 1;
327 // Keep normal transform for other colors
328 cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
329 return 1;
334 // Preserve all K plane.
335 static
336 int BlackPreservingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
339 WORD LabK[4];
340 double SumCMY, SumCMYK, Error;
341 cmsCIELab ColorimetricLab, BlackPreservingLab;
342 BPCARGO* bp = (LPBPCARGO) Cargo;
344 // Get the K across Tone curve
345 LabK[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
347 // If going across black only, keep black only
348 if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
350 Out[0] = Out[1] = Out[2] = 0;
351 Out[3] = LabK[3];
352 return 1;
355 // Try the original transform, maybe K is already ok (valid on K=0)
356 cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
357 if (Out[3] == LabK[3]) return 1;
360 // No, mesure and keep Lab measurement for further usage
361 cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);
363 // Is not black only and the transform doesn't keep black.
364 // Obtain the Lab of CMYK. After that we have Lab + K
365 cmsDoTransform(bp ->cmyk2Lab, In, LabK, 1);
367 // Obtain the corresponding CMY using reverse interpolation.
368 // As a seed, we use the colorimetric CMY
369 cmsEvalLUTreverse(bp ->LabK2cmyk, LabK, Out, Out);
371 // Estimate the error
372 cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
373 Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
376 // Apply TAC if needed
378 SumCMY = Out[0] + Out[1] + Out[2];
379 SumCMYK = SumCMY + Out[3];
381 if (SumCMYK > bp ->MaxTAC) {
383 double Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY);
384 if (Ratio < 0)
385 Ratio = 0;
387 Out[0] = (WORD) floor(Out[0] * Ratio + 0.5); // C
388 Out[1] = (WORD) floor(Out[1] * Ratio + 0.5); // M
389 Out[2] = (WORD) floor(Out[2] * Ratio + 0.5); // Y
392 return 1;
396 // Sample whole gamut to estimate maximum TAC
398 #ifdef _MSC_VER
399 #pragma warning(disable : 4100)
400 #endif
402 static
403 int EstimateTAC(register WORD In[], register WORD Out[], register LPVOID Cargo)
405 BPCARGO* bp = (LPBPCARGO) Cargo;
406 WORD RoundTrip[4];
407 int Sum;
409 cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1);
411 Sum = RoundTrip[0] + RoundTrip[1] + RoundTrip[2] + RoundTrip[3];
413 if (Sum > bp ->MaxTAC)
414 bp ->MaxTAC = Sum;
416 return 1;
420 // Estimate the maximum error
421 static
422 int BlackPreservingEstimateErrorSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
424 BPCARGO* bp = (LPBPCARGO) Cargo;
425 WORD ColorimetricOut[4];
426 cmsCIELab ColorimetricLab, BlackPreservingLab;
427 double Error;
429 if (In[0] == 0 && In[1] == 0 && In[2] == 0) return 1;
431 cmsDoTransform(bp->cmyk2cmyk, In, ColorimetricOut, 1);
433 cmsDoTransform(bp->hProofOutput, ColorimetricOut, &ColorimetricLab, 1);
434 cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
436 Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
438 if (Error > bp ->MaxError)
439 bp ->MaxError = Error;
441 return 1;
444 // Setup the K preservation strategy
445 int LCMSEXPORT cmsSetCMYKPreservationStrategy(int n)
447 int OldVal = GlobalBlackPreservationStrategy;
449 if (n >= 0)
450 GlobalBlackPreservationStrategy = n;
452 return OldVal;
455 #pragma warning(disable: 4550)
457 // Get a pointer to callback on depending of strategy
458 static
459 _cmsSAMPLER _cmsGetBlackPreservationSampler(void)
461 switch (GlobalBlackPreservationStrategy) {
463 case 0: return BlackPreservingGrayOnlySampler;
464 default: return BlackPreservingSampler;
469 // This is the black-preserving devicelink generator
470 LPLUT _cmsPrecalculateBlackPreservingDeviceLink(cmsHTRANSFORM hCMYK2CMYK, DWORD dwFlags)
472 _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK;
473 BPCARGO Cargo;
474 LPLUT Grid;
475 DWORD LocalFlags;
476 cmsHPROFILE hLab = cmsCreateLabProfile(NULL);
477 int nGridPoints;
478 icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual
479 icSigAToB1Tag, // Relative colorimetric
480 icSigAToB2Tag, // Saturation
481 icSigAToB1Tag }; // Absolute colorimetric
482 // (Relative/WhitePoint)
484 nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
486 // Get a copy of inteserting flags for this kind of xform
487 LocalFlags = cmsFLAGS_NOTPRECALC;
488 if (p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION)
489 LocalFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
491 // Fill in cargo struct
492 Cargo.cmyk2cmyk = hCMYK2CMYK;
494 // Compute tone curve.
495 Cargo.KTone = _cmsBuildKToneCurve(hCMYK2CMYK, 256);
496 if (Cargo.KTone == NULL) return NULL;
497 cmsCalcL16Params(Cargo.KTone ->nEntries, &Cargo.KToneParams);
500 // Create a CMYK->Lab "normal" transform on input, without K-preservation
501 Cargo.cmyk2Lab = cmsCreateTransform(p ->InputProfile, TYPE_CMYK_16,
502 hLab, TYPE_Lab_16, p->Intent, LocalFlags);
504 // We are going to use the reverse of proof direction
505 Cargo.LabK2cmyk = cmsReadICCLut(p->OutputProfile, Device2PCS[p->Intent]);
507 // Is there any table available?
508 if (Cargo.LabK2cmyk == NULL) {
510 Grid = NULL;
511 goto Cleanup;
514 // Setup a roundtrip on output profile for TAC estimation
515 Cargo.hRoundTrip = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
516 p ->OutputProfile, TYPE_CMYK_16, p->Intent, cmsFLAGS_NOTPRECALC);
519 // Setup a proof CMYK->Lab on output
520 Cargo.hProofOutput = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
521 hLab, TYPE_Lab_DBL, p->Intent, LocalFlags);
524 // Create an empty LUT for holding K-preserving xform
525 Grid = cmsAllocLUT();
526 if (!Grid) goto Cleanup;
528 Grid = cmsAlloc3DGrid(Grid, nGridPoints, 4, 4);
530 // Setup formatters
531 p -> FromInput = _cmsIdentifyInputFormat(p, TYPE_CMYK_16);
532 p -> ToOutput = _cmsIdentifyOutputFormat(p, TYPE_CMYK_16);
536 // Step #1, estimate TAC
537 Cargo.MaxTAC = 0;
538 if (!cmsSample3DGrid(Grid, EstimateTAC, (LPVOID) &Cargo, 0)) {
540 cmsFreeLUT(Grid);
541 Grid = NULL;
542 goto Cleanup;
546 // Step #2, compute approximation
547 if (!cmsSample3DGrid(Grid, _cmsGetBlackPreservationSampler(), (LPVOID) &Cargo, 0)) {
549 cmsFreeLUT(Grid);
550 Grid = NULL;
551 goto Cleanup;
554 // Step #3, estimate error
555 Cargo.MaxError = 0;
556 cmsSample3DGrid(Grid, BlackPreservingEstimateErrorSampler, (LPVOID) &Cargo, SAMPLER_INSPECT);
559 Cleanup:
561 if (Cargo.cmyk2Lab) cmsDeleteTransform(Cargo.cmyk2Lab);
562 if (Cargo.hRoundTrip) cmsDeleteTransform(Cargo.hRoundTrip);
563 if (Cargo.hProofOutput) cmsDeleteTransform(Cargo.hProofOutput);
565 if (hLab) cmsCloseProfile(hLab);
566 if (Cargo.KTone) cmsFreeGamma(Cargo.KTone);
567 if (Cargo.LabK2cmyk) cmsFreeLUT(Cargo.LabK2cmyk);
569 return Grid;
574 // Fix broken LUT. just to obtain other CMS compatibility
576 static
577 void PatchLUT(LPLUT Grid, WORD At[], WORD Value[],
578 int nChannelsOut, int nChannelsIn)
580 LPL16PARAMS p16 = &Grid -> CLut16params;
581 double px, py, pz, pw;
582 int x0, y0, z0, w0;
583 int i, index;
586 if (Grid ->wFlags & LUT_HASTL1) return; // There is a prelinearization
588 px = ((double) At[0] * (p16->Domain)) / 65535.0;
589 py = ((double) At[1] * (p16->Domain)) / 65535.0;
590 pz = ((double) At[2] * (p16->Domain)) / 65535.0;
591 pw = ((double) At[3] * (p16->Domain)) / 65535.0;
593 x0 = (int) floor(px);
594 y0 = (int) floor(py);
595 z0 = (int) floor(pz);
596 w0 = (int) floor(pw);
598 if (nChannelsIn == 4) {
600 if (((px - x0) != 0) ||
601 ((py - y0) != 0) ||
602 ((pz - z0) != 0) ||
603 ((pw - w0) != 0)) return; // Not on exact node
605 index = p16 -> opta4 * x0 +
606 p16 -> opta3 * y0 +
607 p16 -> opta2 * z0 +
608 p16 -> opta1 * w0;
610 else
611 if (nChannelsIn == 3) {
613 if (((px - x0) != 0) ||
614 ((py - y0) != 0) ||
615 ((pz - z0) != 0)) return; // Not on exact node
617 index = p16 -> opta3 * x0 +
618 p16 -> opta2 * y0 +
619 p16 -> opta1 * z0;
621 else
622 if (nChannelsIn == 1) {
624 if (((px - x0) != 0)) return; // Not on exact node
626 index = p16 -> opta1 * x0;
628 else {
629 cmsSignalError(LCMS_ERRC_ABORTED, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn);
630 return;
633 for (i=0; i < nChannelsOut; i++)
634 Grid -> T[index + i] = Value[i];
640 LCMSBOOL _cmsFixWhiteMisalignment(_LPcmsTRANSFORM p)
643 WORD *WhitePointIn, *WhitePointOut, *BlackPointIn, *BlackPointOut;
644 int nOuts, nIns;
647 if (!p -> DeviceLink) return FALSE;
649 if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC) return FALSE;
650 if ((p ->PreviewProfile != NULL) &&
651 (p ->ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC)) return FALSE;
654 if (!_cmsEndPointsBySpace(p -> EntryColorSpace,
655 &WhitePointIn, &BlackPointIn, &nIns)) return FALSE;
658 if (!_cmsEndPointsBySpace(p -> ExitColorSpace,
659 &WhitePointOut, &BlackPointOut, &nOuts)) return FALSE;
661 // Fix white only
663 PatchLUT(p -> DeviceLink, WhitePointIn, WhitePointOut, nOuts, nIns);
664 // PatchLUT(p -> DeviceLink, BlackPointIn, BlackPointOut, nOuts, nIns);
666 return TRUE;