jdk/src/share/native/sun/java2d/cmm/lcms/cmssamp.c
changeset 2 90ce3da70b43
child 2394 404cbe399601
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     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 
       
    56 // ---------------------------------------------------------------------------------
       
    57 
       
    58 static volatile int GlobalBlackPreservationStrategy = 0;
       
    59 
       
    60 // Quantize a value 0 <= i < MaxSamples
       
    61 
       
    62 WORD _cmsQuantizeVal(double i, int MaxSamples)
       
    63 {
       
    64        double x;
       
    65 
       
    66        x = ((double) i * 65535.) / (double) (MaxSamples - 1);
       
    67 
       
    68        return (WORD) floor(x + .5);
       
    69 }
       
    70 
       
    71 
       
    72 // Is a table linear?
       
    73 
       
    74 int cmsIsLinear(WORD Table[], int nEntries)
       
    75 {
       
    76        register int i;
       
    77        int diff;
       
    78 
       
    79        for (i=0; i < nEntries; i++) {
       
    80 
       
    81            diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries));
       
    82            if (diff > 3)
       
    83                      return 0;
       
    84        }
       
    85 
       
    86        return 1;
       
    87 }
       
    88 
       
    89 
       
    90 
       
    91 // pow() restricted to integer
       
    92 
       
    93 static
       
    94 int ipow(int base, int exp)
       
    95 {
       
    96         int res = base;
       
    97 
       
    98         while (--exp)
       
    99                res *= base;
       
   100 
       
   101         return res;
       
   102 }
       
   103 
       
   104 
       
   105 // Given n, 0<=n<=clut^dim, returns the colorant.
       
   106 
       
   107 static
       
   108 int ComponentOf(int n, int clut, int nColorant)
       
   109 {
       
   110         if (nColorant <= 0)
       
   111                 return (n % clut);
       
   112 
       
   113         n /= ipow(clut, nColorant);
       
   114 
       
   115         return (n % clut);
       
   116 }
       
   117 
       
   118 
       
   119 
       
   120 // This routine does a sweep on whole input space, and calls its callback
       
   121 // function on knots. returns TRUE if all ok, FALSE otherwise.
       
   122 
       
   123 BOOL LCMSEXPORT cmsSample3DGrid(LPLUT Lut, _cmsSAMPLER Sampler, LPVOID Cargo, DWORD dwFlags)
       
   124 {
       
   125    int i, t, nTotalPoints, Colorant, index;
       
   126    WORD In[MAXCHANNELS], Out[MAXCHANNELS];
       
   127 
       
   128    nTotalPoints = ipow(Lut->cLutPoints, Lut -> InputChan);
       
   129 
       
   130    index = 0;
       
   131    for (i = 0; i < nTotalPoints; i++) {
       
   132 
       
   133         for (t=0; t < (int) Lut -> InputChan; t++) {
       
   134 
       
   135                 Colorant =  ComponentOf(i, Lut -> cLutPoints, (Lut -> InputChan - t  - 1 ));
       
   136                 In[t]    = _cmsQuantizeVal(Colorant, Lut -> cLutPoints);
       
   137         }
       
   138 
       
   139 
       
   140         if (dwFlags & SAMPLER_HASTL1) {
       
   141 
       
   142                  for (t=0; t < (int) Lut -> InputChan; t++)
       
   143                      In[t] = cmsReverseLinearInterpLUT16(In[t],
       
   144                                                 Lut -> L1[t],
       
   145                                                 &Lut -> In16params);
       
   146         }
       
   147 
       
   148 
       
   149         // if (dwFlags & SAMPLER_INSPECT) {
       
   150 
       
   151              for (t=0; t < (int) Lut -> OutputChan; t++)
       
   152                         Out[t] = Lut->T[index + t];
       
   153         // }
       
   154 
       
   155 
       
   156         if (!Sampler(In, Out, Cargo))
       
   157                 return FALSE;
       
   158 
       
   159         if (!(dwFlags & SAMPLER_INSPECT)) {
       
   160 
       
   161             if (dwFlags & SAMPLER_HASTL2) {
       
   162 
       
   163                 for (t=0; t < (int) Lut -> OutputChan; t++)
       
   164                      Out[t] = cmsReverseLinearInterpLUT16(Out[t],
       
   165                                                    Lut -> L2[t],
       
   166                                                    &Lut -> Out16params);
       
   167                 }
       
   168 
       
   169 
       
   170             for (t=0; t < (int) Lut -> OutputChan; t++)
       
   171                         Lut->T[index + t] = Out[t];
       
   172 
       
   173         }
       
   174 
       
   175         index += Lut -> OutputChan;
       
   176 
       
   177     }
       
   178 
       
   179     return TRUE;
       
   180 }
       
   181 
       
   182 
       
   183 
       
   184 
       
   185 
       
   186 
       
   187 // choose reasonable resolution
       
   188 int _cmsReasonableGridpointsByColorspace(icColorSpaceSignature Colorspace, DWORD dwFlags)
       
   189 {
       
   190     int nChannels;
       
   191 
       
   192     // Already specified?
       
   193     if (dwFlags & 0x00FF0000) {
       
   194             // Yes, grab'em
       
   195             return (dwFlags >> 16) & 0xFF;
       
   196     }
       
   197 
       
   198     nChannels = _cmsChannelsOf(Colorspace);
       
   199 
       
   200     // HighResPrecalc is maximum resolution
       
   201 
       
   202     if (dwFlags & cmsFLAGS_HIGHRESPRECALC) {
       
   203 
       
   204         if (nChannels > 4)
       
   205                 return 7;       // 7 for Hifi
       
   206 
       
   207         if (nChannels == 4)     // 23 for CMYK
       
   208                 return 23;
       
   209 
       
   210         return 49;      // 49 for RGB and others
       
   211     }
       
   212 
       
   213 
       
   214     // LowResPrecal is stripped resolution
       
   215 
       
   216     if (dwFlags & cmsFLAGS_LOWRESPRECALC) {
       
   217 
       
   218         if (nChannels > 4)
       
   219                 return 6;       // 6 for Hifi
       
   220 
       
   221         if (nChannels == 1)
       
   222                 return 33;      // For monochrome
       
   223 
       
   224         return 17;              // 17 for remaining
       
   225     }
       
   226 
       
   227     // Default values
       
   228 
       
   229     if (nChannels > 4)
       
   230                 return 7;       // 7 for Hifi
       
   231 
       
   232     if (nChannels == 4)
       
   233                 return 17;      // 17 for CMYK
       
   234 
       
   235     return 33;                  // 33 for RGB
       
   236 
       
   237 }
       
   238 
       
   239 // Sampler implemented by another transform. This is a clean way to
       
   240 // precalculate the devicelink 3D CLUT for almost any transform
       
   241 
       
   242 static
       
   243 int XFormSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
       
   244 {
       
   245         cmsDoTransform((cmsHTRANSFORM) Cargo, In, Out, 1);
       
   246         return TRUE;
       
   247 }
       
   248 
       
   249 // This routine does compute the devicelink CLUT containing whole
       
   250 // transform. Handles any channel number.
       
   251 
       
   252 LPLUT _cmsPrecalculateDeviceLink(cmsHTRANSFORM h, DWORD dwFlags)
       
   253 {
       
   254        _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h;
       
   255        LPLUT Grid;
       
   256        int nGridPoints;
       
   257        DWORD dwFormatIn, dwFormatOut;
       
   258        int ChannelsIn, ChannelsOut;
       
   259        LPLUT SaveGamutLUT;
       
   260 
       
   261        // Remove any gamut checking
       
   262        SaveGamutLUT = p ->Gamut;
       
   263        p ->Gamut = NULL;
       
   264 
       
   265        ChannelsIn   = _cmsChannelsOf(p -> EntryColorSpace);
       
   266        ChannelsOut  = _cmsChannelsOf(p -> ExitColorSpace);
       
   267 
       
   268        nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
       
   269 
       
   270        Grid =  cmsAllocLUT();
       
   271        if (!Grid) return NULL;
       
   272 
       
   273        Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsIn, ChannelsOut);
       
   274 
       
   275        // Compute device link on 16-bit basis
       
   276        dwFormatIn   = (CHANNELS_SH(ChannelsIn)|BYTES_SH(2));
       
   277        dwFormatOut  = (CHANNELS_SH(ChannelsOut)|BYTES_SH(2));
       
   278 
       
   279        p -> FromInput = _cmsIdentifyInputFormat(p, dwFormatIn);
       
   280        p -> ToOutput  = _cmsIdentifyOutputFormat(p, dwFormatOut);
       
   281 
       
   282        // Fix gamut & gamma possible mismatches.
       
   283 
       
   284        if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION)) {
       
   285 
       
   286            cmsHTRANSFORM hOne[1];
       
   287            hOne[0] = h;
       
   288 
       
   289            _cmsComputePrelinearizationTablesFromXFORM(hOne, 1, Grid);
       
   290        }
       
   291 
       
   292 
       
   293        // Attention to this typecast! we can take the luxury to
       
   294        // do this since cmsHTRANSFORM is only an alias to a pointer
       
   295        // to the transform struct.
       
   296 
       
   297        if (!cmsSample3DGrid(Grid, XFormSampler, (LPVOID) p, Grid -> wFlags)) {
       
   298 
       
   299                 cmsFreeLUT(Grid);
       
   300                 return NULL;
       
   301        }
       
   302 
       
   303 
       
   304        p ->Gamut = SaveGamutLUT;
       
   305        return Grid;
       
   306 }
       
   307 
       
   308 
       
   309 
       
   310 // Sampler for Black-preserving CMYK->CMYK transforms
       
   311 
       
   312 typedef struct {
       
   313                 cmsHTRANSFORM cmyk2cmyk;
       
   314                 cmsHTRANSFORM cmyk2Lab;
       
   315                 LPGAMMATABLE  KTone;
       
   316                 L16PARAMS     KToneParams;
       
   317                 LPLUT         LabK2cmyk;
       
   318                 double        MaxError;
       
   319 
       
   320                 cmsHTRANSFORM hRoundTrip;
       
   321                 int           MaxTAC;
       
   322 
       
   323                 cmsHTRANSFORM hProofOutput;
       
   324 
       
   325     } BPCARGO, *LPBPCARGO;
       
   326 
       
   327 
       
   328 
       
   329 // Preserve black only if that is the only ink used
       
   330 static
       
   331 int BlackPreservingGrayOnlySampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
       
   332 {
       
   333     BPCARGO* bp = (LPBPCARGO) Cargo;
       
   334 
       
   335     // If going across black only, keep black only
       
   336     if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
       
   337 
       
   338         // TAC does not apply because it is black ink!
       
   339         Out[0] = Out[1] = Out[2] = 0;
       
   340         Out[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
       
   341         return 1;
       
   342     }
       
   343 
       
   344     // Keep normal transform for other colors
       
   345     cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
       
   346     return 1;
       
   347 }
       
   348 
       
   349 
       
   350 
       
   351 // That is our K-preserving callback.
       
   352 static
       
   353 int BlackPreservingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
       
   354 {
       
   355 
       
   356     WORD LabK[4];
       
   357     double SumCMY, SumCMYK, Error;
       
   358     cmsCIELab ColorimetricLab, BlackPreservingLab;
       
   359     BPCARGO* bp = (LPBPCARGO) Cargo;
       
   360 
       
   361     // Get the K across Tone curve
       
   362     LabK[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
       
   363 
       
   364     // If going across black only, keep black only
       
   365     if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
       
   366 
       
   367         Out[0] = Out[1] = Out[2] = 0;
       
   368         Out[3] = LabK[3];
       
   369         return 1;
       
   370     }
       
   371 
       
   372     // Try the original transform, maybe K is already ok (valid on K=0)
       
   373     cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
       
   374     if (Out[3] == LabK[3]) return 1;
       
   375 
       
   376 
       
   377     // No, mesure and keep Lab measurement for further usage
       
   378     cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);
       
   379 
       
   380     // Is not black only and the transform doesn't keep black.
       
   381     // Obtain the Lab of CMYK. After that we have Lab + K
       
   382     cmsDoTransform(bp ->cmyk2Lab, In, LabK, 1);
       
   383 
       
   384     // Obtain the corresponding CMY using reverse interpolation.
       
   385     // As a seed, we use the colorimetric CMY
       
   386     cmsEvalLUTreverse(bp ->LabK2cmyk, LabK, Out, Out);
       
   387 
       
   388     // Estimate the error
       
   389     cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
       
   390     Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
       
   391 
       
   392 
       
   393     // Apply TAC if needed
       
   394 
       
   395     SumCMY   = Out[0]  + Out[1] + Out[2];
       
   396     SumCMYK  = SumCMY + Out[3];
       
   397 
       
   398     if (SumCMYK > bp ->MaxTAC) {
       
   399 
       
   400         double Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY);
       
   401         if (Ratio < 0)
       
   402                   Ratio = 0;
       
   403 
       
   404         Out[0] = (WORD) floor(Out[0] * Ratio + 0.5);     // C
       
   405         Out[1] = (WORD) floor(Out[1] * Ratio + 0.5);     // M
       
   406         Out[2] = (WORD) floor(Out[2] * Ratio + 0.5);     // Y
       
   407     }
       
   408 
       
   409     return 1;
       
   410 }
       
   411 
       
   412 
       
   413 // Sample whole gamut to estimate maximum TAC
       
   414 
       
   415 #ifdef _MSC_VER
       
   416 #pragma warning(disable : 4100)
       
   417 #endif
       
   418 
       
   419 static
       
   420 int EstimateTAC(register WORD In[], register WORD Out[], register LPVOID Cargo)
       
   421 {
       
   422     BPCARGO* bp = (LPBPCARGO) Cargo;
       
   423     WORD RoundTrip[4];
       
   424     int Sum;
       
   425 
       
   426     cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1);
       
   427 
       
   428     Sum = RoundTrip[0] + RoundTrip[1] + RoundTrip[2] + RoundTrip[3];
       
   429 
       
   430     if (Sum > bp ->MaxTAC)
       
   431             bp ->MaxTAC = Sum;
       
   432 
       
   433     return 1;
       
   434 }
       
   435 
       
   436 
       
   437 // Estimate the maximum error
       
   438 static
       
   439 int BlackPreservingEstimateErrorSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
       
   440 {
       
   441     BPCARGO* bp = (LPBPCARGO) Cargo;
       
   442     WORD ColorimetricOut[4];
       
   443     cmsCIELab ColorimetricLab, BlackPreservingLab;
       
   444     double Error;
       
   445 
       
   446     if (In[0] == 0 && In[1] == 0 && In[2] == 0) return 1;
       
   447 
       
   448     cmsDoTransform(bp->cmyk2cmyk, In, ColorimetricOut, 1);
       
   449 
       
   450     cmsDoTransform(bp->hProofOutput, ColorimetricOut, &ColorimetricLab, 1);
       
   451     cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
       
   452 
       
   453     Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
       
   454 
       
   455     if (Error > bp ->MaxError)
       
   456         bp ->MaxError = Error;
       
   457 
       
   458     return 1;
       
   459 }
       
   460 
       
   461 // Setup the K preservation strategy
       
   462 int LCMSEXPORT cmsSetCMYKPreservationStrategy(int n)
       
   463 {
       
   464     int OldVal = GlobalBlackPreservationStrategy;
       
   465 
       
   466     if (n >= 0)
       
   467             GlobalBlackPreservationStrategy = n;
       
   468 
       
   469     return OldVal;
       
   470 }
       
   471 
       
   472 
       
   473 // Get a pointer to callback on depending of strategy
       
   474 static
       
   475 _cmsSAMPLER _cmsGetBlackPreservationSampler(void)
       
   476 {
       
   477     switch (GlobalBlackPreservationStrategy) {
       
   478 
       
   479         case 0: return BlackPreservingGrayOnlySampler;
       
   480         default: return BlackPreservingSampler;
       
   481    }
       
   482 
       
   483 }
       
   484 
       
   485 // This is the black-preserving devicelink generator
       
   486 LPLUT _cmsPrecalculateBlackPreservingDeviceLink(cmsHTRANSFORM hCMYK2CMYK, DWORD dwFlags)
       
   487 {
       
   488        _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK;
       
   489        BPCARGO Cargo;
       
   490        LPLUT Grid;
       
   491        DWORD LocalFlags;
       
   492        cmsHPROFILE hLab = cmsCreateLabProfile(NULL);
       
   493        int nGridPoints;
       
   494        icTagSignature Device2PCS[] = {icSigAToB0Tag,       // Perceptual
       
   495                                       icSigAToB1Tag,       // Relative colorimetric
       
   496                                       icSigAToB2Tag,       // Saturation
       
   497                                       icSigAToB1Tag };     // Absolute colorimetric
       
   498                                                            // (Relative/WhitePoint)
       
   499 
       
   500        nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
       
   501 
       
   502        // Get a copy of inteserting flags for this kind of xform
       
   503        LocalFlags = cmsFLAGS_NOTPRECALC;
       
   504        if (p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION)
       
   505            LocalFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
       
   506 
       
   507 
       
   508        // Fill in cargo struct
       
   509        Cargo.cmyk2cmyk = hCMYK2CMYK;
       
   510 
       
   511        // Compute tone curve
       
   512        Cargo.KTone  =  _cmsBuildKToneCurve(hCMYK2CMYK, 256);
       
   513        if (Cargo.KTone == NULL) return NULL;
       
   514        cmsCalcL16Params(Cargo.KTone ->nEntries, &Cargo.KToneParams);
       
   515 
       
   516 
       
   517        // Create a CMYK->Lab "normal" transform on input, without K-preservation
       
   518        Cargo.cmyk2Lab  = cmsCreateTransform(p ->InputProfile, TYPE_CMYK_16,
       
   519                                             hLab, TYPE_Lab_16, p->Intent, LocalFlags);
       
   520 
       
   521        // We are going to use the reverse of proof direction
       
   522        Cargo.LabK2cmyk = cmsReadICCLut(p->OutputProfile, Device2PCS[p->Intent]);
       
   523 
       
   524        // Is there any table available?
       
   525            if (Cargo.LabK2cmyk == NULL) {
       
   526 
       
   527                    Grid = NULL;
       
   528            goto Cleanup;
       
   529            }
       
   530 
       
   531        // Setup a roundtrip on output profile for TAC estimation
       
   532        Cargo.hRoundTrip = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
       
   533                                              p ->OutputProfile, TYPE_CMYK_16, p->Intent, cmsFLAGS_NOTPRECALC);
       
   534 
       
   535 
       
   536        // Setup a proof CMYK->Lab on output
       
   537        Cargo.hProofOutput  = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
       
   538                                             hLab, TYPE_Lab_DBL, p->Intent, LocalFlags);
       
   539 
       
   540 
       
   541        // Create an empty LUT for holding K-preserving xform
       
   542        Grid =  cmsAllocLUT();
       
   543        if (!Grid) goto Cleanup;
       
   544 
       
   545        Grid = cmsAlloc3DGrid(Grid, nGridPoints, 4, 4);
       
   546 
       
   547        // Setup formatters
       
   548        p -> FromInput = _cmsIdentifyInputFormat(p,  TYPE_CMYK_16);
       
   549        p -> ToOutput  = _cmsIdentifyOutputFormat(p, TYPE_CMYK_16);
       
   550 
       
   551 
       
   552 
       
   553        // Step #1, estimate TAC
       
   554        Cargo.MaxTAC = 0;
       
   555        if (!cmsSample3DGrid(Grid, EstimateTAC, (LPVOID) &Cargo, 0)) {
       
   556 
       
   557                 cmsFreeLUT(Grid);
       
   558                 Grid = NULL;
       
   559                 goto Cleanup;
       
   560        }
       
   561 
       
   562 
       
   563        // Step #2, compute approximation
       
   564        if (!cmsSample3DGrid(Grid, _cmsGetBlackPreservationSampler(), (LPVOID) &Cargo, 0)) {
       
   565 
       
   566                 cmsFreeLUT(Grid);
       
   567                 Grid = NULL;
       
   568                 goto Cleanup;
       
   569        }
       
   570 
       
   571        // Step #3, estimate error
       
   572         Cargo.MaxError = 0;
       
   573         cmsSample3DGrid(Grid, BlackPreservingEstimateErrorSampler, (LPVOID) &Cargo, SAMPLER_INSPECT);
       
   574 
       
   575 
       
   576 Cleanup:
       
   577 
       
   578        if (Cargo.cmyk2Lab) cmsDeleteTransform(Cargo.cmyk2Lab);
       
   579        if (Cargo.hRoundTrip) cmsDeleteTransform(Cargo.hRoundTrip);
       
   580        if (Cargo.hProofOutput) cmsDeleteTransform(Cargo.hProofOutput);
       
   581 
       
   582        if (hLab) cmsCloseProfile(hLab);
       
   583        if (Cargo.KTone) cmsFreeGamma(Cargo.KTone);
       
   584        if (Cargo.LabK2cmyk) cmsFreeLUT(Cargo.LabK2cmyk);
       
   585 
       
   586        return Grid;
       
   587 }
       
   588 
       
   589 
       
   590 
       
   591 // Fix broken LUT. just to obtain other CMS compatibility
       
   592 
       
   593 static
       
   594 void PatchLUT(LPLUT Grid, WORD At[], WORD Value[],
       
   595                      int nChannelsOut, int nChannelsIn)
       
   596 {
       
   597        LPL16PARAMS p16  = &Grid -> CLut16params;
       
   598        double     px, py, pz, pw;
       
   599        int        x0, y0, z0, w0;
       
   600        int        i, index;
       
   601 
       
   602 
       
   603        if (Grid ->wFlags & LUT_HASTL1) return;  // There is a prelinearization
       
   604 
       
   605        px = ((double) At[0] * (p16->Domain)) / 65535.0;
       
   606        py = ((double) At[1] * (p16->Domain)) / 65535.0;
       
   607        pz = ((double) At[2] * (p16->Domain)) / 65535.0;
       
   608        pw = ((double) At[3] * (p16->Domain)) / 65535.0;
       
   609 
       
   610        x0 = (int) floor(px);
       
   611        y0 = (int) floor(py);
       
   612        z0 = (int) floor(pz);
       
   613        w0 = (int) floor(pw);
       
   614 
       
   615        if (nChannelsIn == 4) {
       
   616 
       
   617               if (((px - x0) != 0) ||
       
   618                   ((py - y0) != 0) ||
       
   619                   ((pz - z0) != 0) ||
       
   620                   ((pw - w0) != 0)) return; // Not on exact node
       
   621 
       
   622               index = p16 -> opta4 * x0 +
       
   623                       p16 -> opta3 * y0 +
       
   624                       p16 -> opta2 * z0 +
       
   625                       p16 -> opta1 * w0;
       
   626        }
       
   627        else
       
   628        if (nChannelsIn == 3) {
       
   629 
       
   630               if (((px - x0) != 0) ||
       
   631                   ((py - y0) != 0) ||
       
   632                   ((pz - z0) != 0)) return;  // Not on exact node
       
   633 
       
   634               index = p16 -> opta3 * x0 +
       
   635                       p16 -> opta2 * y0 +
       
   636                       p16 -> opta1 * z0;
       
   637        }
       
   638        else
       
   639        if (nChannelsIn == 1) {
       
   640 
       
   641               if (((px - x0) != 0)) return; // Not on exact node
       
   642 
       
   643               index = p16 -> opta1 * x0;
       
   644        }
       
   645        else {
       
   646            cmsSignalError(LCMS_ERRC_ABORTED, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn);
       
   647            return;
       
   648        }
       
   649 
       
   650        for (i=0; i < nChannelsOut; i++)
       
   651               Grid -> T[index + i] = Value[i];
       
   652 
       
   653 }
       
   654 
       
   655 
       
   656 
       
   657 BOOL _cmsFixWhiteMisalignment(_LPcmsTRANSFORM p)
       
   658 {
       
   659 
       
   660        WORD *WhitePointIn, *WhitePointOut, *BlackPointIn, *BlackPointOut;
       
   661        int nOuts, nIns;
       
   662 
       
   663 
       
   664        if (!p -> DeviceLink) return FALSE;
       
   665 
       
   666        if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC) return FALSE;
       
   667        if ((p ->PreviewProfile != NULL) &&
       
   668            (p ->ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC)) return FALSE;
       
   669 
       
   670 
       
   671        if (!_cmsEndPointsBySpace(p -> EntryColorSpace,
       
   672                                  &WhitePointIn, &BlackPointIn, &nIns)) return FALSE;
       
   673 
       
   674 
       
   675        if (!_cmsEndPointsBySpace(p -> ExitColorSpace,
       
   676                                    &WhitePointOut, &BlackPointOut, &nOuts)) return FALSE;
       
   677 
       
   678        // Fix white only
       
   679 
       
   680        PatchLUT(p -> DeviceLink, WhitePointIn, WhitePointOut, nOuts, nIns);
       
   681        // PatchLUT(p -> DeviceLink, BlackPointIn, BlackPointOut, nOuts, nIns);
       
   682 
       
   683        return TRUE;
       
   684 }