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.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * 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 //
  32 //  Little Color Management System
  33 //  Copyright (c) 1998-2012 Marti Maria Saguer
  34 //
  35 // Permission is hereby granted, free of charge, to any person obtaining
  36 // a copy of this software and associated documentation files (the "Software"),
  37 // to deal in the Software without restriction, including without limitation
  38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  39 // and/or sell copies of the Software, and to permit persons to whom the Software
  40 // is furnished to do so, subject to the following conditions:
  41 //
  42 // The above copyright notice and this permission notice shall be included in
  43 // all copies or substantial portions of the Software.
  44 //
  45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  52 //
  53 //---------------------------------------------------------------------------------
  54 //
  55 
  56 #include "lcms2_internal.h"
  57 
  58 
  59 // Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point
  60 // compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS
  61 // after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1)
  62 cmsPipeline* _cmsLinkProfiles(cmsContext     ContextID,
  63                               cmsUInt32Number nProfiles,
  64                               cmsUInt32Number Intents[],
  65                               cmsHPROFILE     hProfiles[],
  66                               cmsBool         BPC[],
  67                               cmsFloat64Number AdaptationStates[],
  68                               cmsUInt32Number dwFlags);
  69 
  70 //---------------------------------------------------------------------------------
  71 
  72 // This is the default routine for ICC-style intents. A user may decide to override it by using a plugin.
  73 // Supported intents are perceptual, relative colorimetric, saturation and ICC-absolute colorimetric
  74 static
  75 cmsPipeline* DefaultICCintents(cmsContext     ContextID,
  76                                cmsUInt32Number nProfiles,
  77                                cmsUInt32Number Intents[],
  78                                cmsHPROFILE     hProfiles[],
  79                                cmsBool         BPC[],
  80                                cmsFloat64Number AdaptationStates[],
  81                                cmsUInt32Number dwFlags);
  82 
  83 //---------------------------------------------------------------------------------
  84 
  85 // This is the entry for black-preserving K-only intents, which are non-ICC. Last profile have to be a output profile
  86 // to do the trick (no devicelinks allowed at that position)
  87 static
  88 cmsPipeline*  BlackPreservingKOnlyIntents(cmsContext     ContextID,
  89                                           cmsUInt32Number nProfiles,
  90                                           cmsUInt32Number Intents[],
  91                                           cmsHPROFILE     hProfiles[],
  92                                           cmsBool         BPC[],
  93                                           cmsFloat64Number AdaptationStates[],
  94                                           cmsUInt32Number dwFlags);
  95 
  96 //---------------------------------------------------------------------------------
  97 
  98 // This is the entry for black-plane preserving, which are non-ICC. Again, Last profile have to be a output profile
  99 // to do the trick (no devicelinks allowed at that position)
 100 static
 101 cmsPipeline*  BlackPreservingKPlaneIntents(cmsContext     ContextID,
 102                                            cmsUInt32Number nProfiles,
 103                                            cmsUInt32Number Intents[],
 104                                            cmsHPROFILE     hProfiles[],
 105                                            cmsBool         BPC[],
 106                                            cmsFloat64Number AdaptationStates[],
 107                                            cmsUInt32Number dwFlags);
 108 
 109 //---------------------------------------------------------------------------------
 110 
 111 
 112 // This is a structure holding implementations for all supported intents.
 113 typedef struct _cms_intents_list {
 114 
 115     cmsUInt32Number Intent;
 116     char            Description[256];
 117     cmsIntentFn     Link;
 118     struct _cms_intents_list*  Next;
 119 
 120 } cmsIntentsList;
 121 
 122 
 123 // Built-in intents
 124 static cmsIntentsList DefaultIntents[] = {
 125 
 126     { INTENT_PERCEPTUAL,                            "Perceptual",                                   DefaultICCintents,            &DefaultIntents[1] },
 127     { INTENT_RELATIVE_COLORIMETRIC,                 "Relative colorimetric",                        DefaultICCintents,            &DefaultIntents[2] },
 128     { INTENT_SATURATION,                            "Saturation",                                   DefaultICCintents,            &DefaultIntents[3] },
 129     { INTENT_ABSOLUTE_COLORIMETRIC,                 "Absolute colorimetric",                        DefaultICCintents,            &DefaultIntents[4] },
 130     { INTENT_PRESERVE_K_ONLY_PERCEPTUAL,            "Perceptual preserving black ink",              BlackPreservingKOnlyIntents,  &DefaultIntents[5] },
 131     { INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC, "Relative colorimetric preserving black ink",   BlackPreservingKOnlyIntents,  &DefaultIntents[6] },
 132     { INTENT_PRESERVE_K_ONLY_SATURATION,            "Saturation preserving black ink",              BlackPreservingKOnlyIntents,  &DefaultIntents[7] },
 133     { INTENT_PRESERVE_K_PLANE_PERCEPTUAL,           "Perceptual preserving black plane",            BlackPreservingKPlaneIntents, &DefaultIntents[8] },
 134     { INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC,"Relative colorimetric preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[9] },
 135     { INTENT_PRESERVE_K_PLANE_SATURATION,           "Saturation preserving black plane",            BlackPreservingKPlaneIntents, NULL }
 136 };
 137 
 138 
 139 // A pointer to the begining of the list
 140 static cmsIntentsList *Intents = DefaultIntents;
 141 
 142 // Search the list for a suitable intent. Returns NULL if not found
 143 static
 144 cmsIntentsList* SearchIntent(cmsUInt32Number Intent)
 145 {
 146     cmsIntentsList* pt;
 147 
 148     for (pt = Intents; pt != NULL; pt = pt -> Next)
 149         if (pt ->Intent == Intent) return pt;
 150 
 151     return NULL;
 152 }
 153 
 154 // Black point compensation. Implemented as a linear scaling in XYZ. Black points
 155 // should come relative to the white point. Fills an matrix/offset element m
 156 // which is organized as a 4x4 matrix.
 157 static
 158 void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn,
 159                                    const cmsCIEXYZ* BlackPointOut,
 160                                    cmsMAT3* m, cmsVEC3* off)
 161 {
 162   cmsFloat64Number ax, ay, az, bx, by, bz, tx, ty, tz;
 163 
 164    // Now we need to compute a matrix plus an offset m and of such of
 165    // [m]*bpin + off = bpout
 166    // [m]*D50  + off = D50
 167    //
 168    // This is a linear scaling in the form ax+b, where
 169    // a = (bpout - D50) / (bpin - D50)
 170    // b = - D50* (bpout - bpin) / (bpin - D50)
 171 
 172    tx = BlackPointIn->X - cmsD50_XYZ()->X;
 173    ty = BlackPointIn->Y - cmsD50_XYZ()->Y;
 174    tz = BlackPointIn->Z - cmsD50_XYZ()->Z;
 175 
 176    ax = (BlackPointOut->X - cmsD50_XYZ()->X) / tx;
 177    ay = (BlackPointOut->Y - cmsD50_XYZ()->Y) / ty;
 178    az = (BlackPointOut->Z - cmsD50_XYZ()->Z) / tz;
 179 
 180    bx = - cmsD50_XYZ()-> X * (BlackPointOut->X - BlackPointIn->X) / tx;
 181    by = - cmsD50_XYZ()-> Y * (BlackPointOut->Y - BlackPointIn->Y) / ty;
 182    bz = - cmsD50_XYZ()-> Z * (BlackPointOut->Z - BlackPointIn->Z) / tz;
 183 
 184    _cmsVEC3init(&m ->v[0], ax, 0,  0);
 185    _cmsVEC3init(&m ->v[1], 0, ay,  0);
 186    _cmsVEC3init(&m ->v[2], 0,  0,  az);
 187    _cmsVEC3init(off, bx, by, bz);
 188 
 189 }
 190 
 191 
 192 // Approximate a blackbody illuminant based on CHAD information
 193 static
 194 cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad)
 195 {
 196     // Convert D50 across inverse CHAD to get the absolute white point
 197     cmsVEC3 d, s;
 198     cmsCIEXYZ Dest;
 199     cmsCIExyY DestChromaticity;
 200     cmsFloat64Number TempK;
 201     cmsMAT3 m1, m2;
 202 
 203     m1 = *Chad;
 204     if (!_cmsMAT3inverse(&m1, &m2)) return FALSE;
 205 
 206     s.n[VX] = cmsD50_XYZ() -> X;
 207     s.n[VY] = cmsD50_XYZ() -> Y;
 208     s.n[VZ] = cmsD50_XYZ() -> Z;
 209 
 210     _cmsMAT3eval(&d, &m2, &s);
 211 
 212     Dest.X = d.n[VX];
 213     Dest.Y = d.n[VY];
 214     Dest.Z = d.n[VZ];
 215 
 216     cmsXYZ2xyY(&DestChromaticity, &Dest);
 217 
 218     if (!cmsTempFromWhitePoint(&TempK, &DestChromaticity))
 219         return -1.0;
 220 
 221     return TempK;
 222 }
 223 
 224 // Compute a CHAD based on a given temperature
 225 static
 226     void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp)
 227 {
 228     cmsCIEXYZ White;
 229     cmsCIExyY ChromaticityOfWhite;
 230 
 231     cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp);
 232     cmsxyY2XYZ(&White, &ChromaticityOfWhite);
 233     _cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ());
 234 }
 235 
 236 // Join scalings to obtain relative input to absolute and then to relative output.
 237 // Result is stored in a 3x3 matrix
 238 static
 239 cmsBool  ComputeAbsoluteIntent(cmsFloat64Number AdaptationState,
 240                                const cmsCIEXYZ* WhitePointIn,
 241                                const cmsMAT3* ChromaticAdaptationMatrixIn,
 242                                const cmsCIEXYZ* WhitePointOut,
 243                                const cmsMAT3* ChromaticAdaptationMatrixOut,
 244                                cmsMAT3* m)
 245 {
 246     cmsMAT3 Scale, m1, m2, m3, m4;
 247 
 248     // Adaptation state
 249     if (AdaptationState == 1.0) {
 250 
 251         // Observer is fully adapted. Keep chromatic adaptation.
 252         // That is the standard V4 behaviour
 253         _cmsVEC3init(&m->v[0], WhitePointIn->X / WhitePointOut->X, 0, 0);
 254         _cmsVEC3init(&m->v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0);
 255         _cmsVEC3init(&m->v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z);
 256 
 257     }
 258     else  {
 259 
 260         // Incomplete adaptation. This is an advanced feature.
 261         _cmsVEC3init(&Scale.v[0], WhitePointIn->X / WhitePointOut->X, 0, 0);
 262         _cmsVEC3init(&Scale.v[1], 0,  WhitePointIn->Y / WhitePointOut->Y, 0);
 263         _cmsVEC3init(&Scale.v[2], 0, 0,  WhitePointIn->Z / WhitePointOut->Z);
 264 
 265 
 266         if (AdaptationState == 0.0) {
 267 
 268             m1 = *ChromaticAdaptationMatrixOut;
 269             _cmsMAT3per(&m2, &m1, &Scale);
 270             // m2 holds CHAD from output white to D50 times abs. col. scaling
 271 
 272             // Observer is not adapted, undo the chromatic adaptation
 273             _cmsMAT3per(m, &m3, ChromaticAdaptationMatrixOut);
 274 
 275             m3 = *ChromaticAdaptationMatrixIn;
 276             if (!_cmsMAT3inverse(&m3, &m4)) return FALSE;
 277             _cmsMAT3per(m, &m2, &m4);
 278 
 279         } else {
 280 
 281             cmsMAT3 MixedCHAD;
 282             cmsFloat64Number TempSrc, TempDest, Temp;
 283 
 284             m1 = *ChromaticAdaptationMatrixIn;
 285             if (!_cmsMAT3inverse(&m1, &m2)) return FALSE;
 286             _cmsMAT3per(&m3, &m2, &Scale);
 287             // m3 holds CHAD from input white to D50 times abs. col. scaling
 288 
 289             TempSrc  = CHAD2Temp(ChromaticAdaptationMatrixIn);
 290             TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut);
 291 
 292             if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong
 293 
 294             if (_cmsMAT3isIdentity(&Scale) && fabs(TempSrc - TempDest) < 0.01) {
 295 
 296                 _cmsMAT3identity(m);
 297                 return TRUE;
 298             }
 299 
 300             Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc;
 301 
 302             // Get a CHAD from whatever output temperature to D50. This replaces output CHAD
 303             Temp2CHAD(&MixedCHAD, Temp);
 304 
 305             _cmsMAT3per(m, &m3, &MixedCHAD);
 306         }
 307 
 308     }
 309     return TRUE;
 310 
 311 }
 312 
 313 // Just to see if m matrix should be applied
 314 static
 315 cmsBool IsEmptyLayer(cmsMAT3* m, cmsVEC3* off)
 316 {
 317     cmsFloat64Number diff = 0;
 318     cmsMAT3 Ident;
 319     int i;
 320 
 321     if (m == NULL && off == NULL) return TRUE;  // NULL is allowed as an empty layer
 322     if (m == NULL && off != NULL) return FALSE; // This is an internal error
 323 
 324     _cmsMAT3identity(&Ident);
 325 
 326     for (i=0; i < 3*3; i++)
 327         diff += fabs(((cmsFloat64Number*)m)[i] - ((cmsFloat64Number*)&Ident)[i]);
 328 
 329     for (i=0; i < 3; i++)
 330         diff += fabs(((cmsFloat64Number*)off)[i]);
 331 
 332 
 333     return (diff < 0.002);
 334 }
 335 
 336 
 337 // Compute the conversion layer
 338 static
 339 cmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[],
 340                                  cmsUInt32Number Intent,
 341                                  cmsBool BPC,
 342                                  cmsFloat64Number AdaptationState,
 343                                  cmsMAT3* m, cmsVEC3* off)
 344 {
 345 
 346     int k;
 347 
 348     // m  and off are set to identity and this is detected latter on
 349     _cmsMAT3identity(m);
 350     _cmsVEC3init(off, 0, 0, 0);
 351 
 352     // If intent is abs. colorimetric,
 353     if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) {
 354 
 355         cmsCIEXYZ WhitePointIn, WhitePointOut;
 356         cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut;
 357 
 358         _cmsReadMediaWhitePoint(&WhitePointIn,  hProfiles[i-1]);
 359         _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]);
 360 
 361         _cmsReadMediaWhitePoint(&WhitePointOut,  hProfiles[i]);
 362         _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]);
 363 
 364         if (!ComputeAbsoluteIntent(AdaptationState,
 365                                   &WhitePointIn,  &ChromaticAdaptationMatrixIn,
 366                                   &WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE;
 367 
 368     }
 369     else {
 370         // Rest of intents may apply BPC.
 371 
 372         if (BPC) {
 373 
 374             cmsCIEXYZ BlackPointIn, BlackPointOut;
 375 
 376             cmsDetectBlackPoint(&BlackPointIn,  hProfiles[i-1], Intent, 0);
 377             cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0);
 378 
 379             // If black points are equal, then do nothing
 380             if (BlackPointIn.X != BlackPointOut.X ||
 381                 BlackPointIn.Y != BlackPointOut.Y ||
 382                 BlackPointIn.Z != BlackPointOut.Z)
 383                     ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off);
 384         }
 385     }
 386 
 387     // Offset should be adjusted because the encoding. We encode XYZ normalized to 0..1.0,
 388     // to do that, we divide by MAX_ENCODEABLE_XZY. The conversion stage goes XYZ -> XYZ so
 389     // we have first to convert from encoded to XYZ and then convert back to encoded.
 390     // y = Mx + Off
 391     // x = x'c
 392     // y = M x'c + Off
 393     // y = y'c; y' = y / c
 394     // y' = (Mx'c + Off) /c = Mx' + (Off / c)
 395 
 396     for (k=0; k < 3; k++) {
 397         off ->n[k] /= MAX_ENCODEABLE_XYZ;
 398     }
 399 
 400     return TRUE;
 401 }
 402 
 403 
 404 // Add a conversion stage if needed. If a matrix/offset m is given, it applies to XYZ space
 405 static
 406 cmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColorSpaceSignature OutPCS, cmsMAT3* m, cmsVEC3* off)
 407 {
 408     cmsFloat64Number* m_as_dbl = (cmsFloat64Number*) m;
 409     cmsFloat64Number* off_as_dbl = (cmsFloat64Number*) off;
 410 
 411     // Handle PCS mismatches. A specialized stage is added to the LUT in such case
 412     switch (InPCS) {
 413 
 414         case cmsSigXYZData: // Input profile operates in XYZ
 415 
 416             switch (OutPCS) {
 417 
 418             case cmsSigXYZData:  // XYZ -> XYZ
 419                 if (!IsEmptyLayer(m, off))
 420                     cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl));
 421                 break;
 422 
 423             case cmsSigLabData:  // XYZ -> Lab
 424                 if (!IsEmptyLayer(m, off))
 425                     cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl));
 426                 cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID));
 427                 break;
 428 
 429             default:
 430                 return FALSE;   // Colorspace mismatch
 431                 }
 432                 break;
 433 
 434 
 435         case cmsSigLabData: // Input profile operates in Lab
 436 
 437             switch (OutPCS) {
 438 
 439             case cmsSigXYZData:  // Lab -> XYZ
 440 
 441                 cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID));
 442                 if (!IsEmptyLayer(m, off))
 443                     cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl));
 444                 break;
 445 
 446             case cmsSigLabData:  // Lab -> Lab
 447 
 448                 if (!IsEmptyLayer(m, off)) {
 449                     cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID));
 450                     cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl));
 451                     cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID));
 452                 }
 453                 break;
 454 
 455             default:
 456                 return FALSE;  // Mismatch
 457             }
 458             break;
 459 
 460 
 461             // On colorspaces other than PCS, check for same space
 462         default:
 463             if (InPCS != OutPCS) return FALSE;
 464             break;
 465     }
 466 
 467     return TRUE;
 468 }
 469 
 470 
 471 // Is a given space compatible with another?
 472 static
 473 cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature b)
 474 {
 475     // If they are same, they are compatible.
 476     if (a == b) return TRUE;
 477 
 478     // Check for MCH4 substitution of CMYK
 479     if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE;
 480     if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE;
 481 
 482     // Check for XYZ/Lab. Those spaces are interchangeable as they can be computed one from other.
 483     if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE;
 484     if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE;
 485 
 486     return FALSE;
 487 }
 488 
 489 
 490 // Default handler for ICC-style intents
 491 static
 492 cmsPipeline* DefaultICCintents(cmsContext       ContextID,
 493                                cmsUInt32Number  nProfiles,
 494                                cmsUInt32Number  TheIntents[],
 495                                cmsHPROFILE      hProfiles[],
 496                                cmsBool          BPC[],
 497                                cmsFloat64Number AdaptationStates[],
 498                                cmsUInt32Number  dwFlags)
 499 {
 500     cmsPipeline* Lut, *Result;
 501     cmsHPROFILE hProfile;
 502     cmsMAT3 m;
 503     cmsVEC3 off;
 504     cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut, CurrentColorSpace;
 505     cmsProfileClassSignature ClassSig;
 506     cmsUInt32Number  i, Intent;
 507 
 508     // For safety
 509     if (nProfiles == 0) return NULL;
 510 
 511     // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined'
 512     Result = cmsPipelineAlloc(ContextID, 0, 0);
 513     if (Result == NULL) return NULL;
 514 
 515     CurrentColorSpace = cmsGetColorSpace(hProfiles[0]);
 516 
 517     for (i=0; i < nProfiles; i++) {
 518 
 519         cmsBool  lIsDeviceLink, lIsInput;
 520 
 521         hProfile      = hProfiles[i];
 522         ClassSig      = cmsGetDeviceClass(hProfile);
 523         lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass );
 524 
 525         // First profile is used as input unless devicelink or abstract
 526         if ((i == 0) && !lIsDeviceLink) {
 527             lIsInput = TRUE;
 528         }
 529         else {
 530           // Else use profile in the input direction if current space is not PCS
 531         lIsInput      = (CurrentColorSpace != cmsSigXYZData) &&
 532                         (CurrentColorSpace != cmsSigLabData);
 533         }
 534 
 535         Intent        = TheIntents[i];
 536 
 537         if (lIsInput || lIsDeviceLink) {
 538 
 539             ColorSpaceIn    = cmsGetColorSpace(hProfile);
 540             ColorSpaceOut   = cmsGetPCS(hProfile);
 541         }
 542         else {
 543 
 544             ColorSpaceIn    = cmsGetPCS(hProfile);
 545             ColorSpaceOut   = cmsGetColorSpace(hProfile);
 546         }
 547 
 548         if (!ColorSpaceIsCompatible(ColorSpaceIn, CurrentColorSpace)) {
 549 
 550             cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "ColorSpace mismatch");
 551             goto Error;
 552         }
 553 
 554         // If devicelink is found, then no custom intent is allowed and we can
 555         // read the LUT to be applied. Settings don't apply here.
 556         if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) {
 557 
 558             // Get the involved LUT from the profile
 559             Lut = _cmsReadDevicelinkLUT(hProfile, Intent);
 560             if (Lut == NULL) goto Error;
 561 
 562             // What about abstract profiles?
 563              if (ClassSig == cmsSigAbstractClass && i > 0) {
 564                 if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error;
 565              }
 566              else {
 567                 _cmsMAT3identity(&m);
 568                 _cmsVEC3init(&off, 0, 0, 0);
 569              }
 570 
 571 
 572             if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error;
 573 
 574         }
 575         else {
 576 
 577             if (lIsInput) {
 578                 // Input direction means non-pcs connection, so proceed like devicelinks
 579                 Lut = _cmsReadInputLUT(hProfile, Intent);
 580                 if (Lut == NULL) goto Error;
 581             }
 582             else {
 583 
 584                 // Output direction means PCS connection. Intent may apply here
 585                 Lut = _cmsReadOutputLUT(hProfile, Intent);
 586                 if (Lut == NULL) goto Error;
 587 
 588 
 589                 if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error;
 590                 if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error;
 591 
 592             }
 593         }
 594 
 595         // Concatenate to the output LUT
 596         cmsPipelineCat(Result, Lut);
 597         cmsPipelineFree(Lut);
 598 
 599         // Update current space
 600         CurrentColorSpace = ColorSpaceOut;
 601     }
 602 
 603     return Result;
 604 
 605 Error:
 606 
 607     if (Result != NULL) cmsPipelineFree(Result);
 608     return NULL;
 609 
 610     cmsUNUSED_PARAMETER(dwFlags);
 611 }
 612 
 613 
 614 // Wrapper for DLL calling convention
 615 cmsPipeline*  CMSEXPORT _cmsDefaultICCintents(cmsContext     ContextID,
 616                                               cmsUInt32Number nProfiles,
 617                                               cmsUInt32Number TheIntents[],
 618                                               cmsHPROFILE     hProfiles[],
 619                                               cmsBool         BPC[],
 620                                               cmsFloat64Number AdaptationStates[],
 621                                               cmsUInt32Number dwFlags)
 622 {
 623     return DefaultICCintents(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags);
 624 }
 625 
 626 // Black preserving intents ---------------------------------------------------------------------------------------------
 627 
 628 // Translate black-preserving intents to ICC ones
 629 static
 630 int TranslateNonICCIntents(int Intent)
 631 {
 632     switch (Intent) {
 633         case INTENT_PRESERVE_K_ONLY_PERCEPTUAL:
 634         case INTENT_PRESERVE_K_PLANE_PERCEPTUAL:
 635             return INTENT_PERCEPTUAL;
 636 
 637         case INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC:
 638         case INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC:
 639             return INTENT_RELATIVE_COLORIMETRIC;
 640 
 641         case INTENT_PRESERVE_K_ONLY_SATURATION:
 642         case INTENT_PRESERVE_K_PLANE_SATURATION:
 643             return INTENT_SATURATION;
 644 
 645         default: return Intent;
 646     }
 647 }
 648 
 649 // Sampler for Black-only preserving CMYK->CMYK transforms
 650 
 651 typedef struct {
 652     cmsPipeline*    cmyk2cmyk;      // The original transform
 653     cmsToneCurve*   KTone;          // Black-to-black tone curve
 654 
 655 } GrayOnlyParams;
 656 
 657 
 658 // Preserve black only if that is the only ink used
 659 static
 660 int BlackPreservingGrayOnlySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
 661 {
 662     GrayOnlyParams* bp = (GrayOnlyParams*) Cargo;
 663 
 664     // If going across black only, keep black only
 665     if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
 666 
 667         // TAC does not apply because it is black ink!
 668         Out[0] = Out[1] = Out[2] = 0;
 669         Out[3] = cmsEvalToneCurve16(bp->KTone, In[3]);
 670         return TRUE;
 671     }
 672 
 673     // Keep normal transform for other colors
 674     bp ->cmyk2cmyk ->Eval16Fn(In, Out, bp ->cmyk2cmyk->Data);
 675     return TRUE;
 676 }
 677 
 678 // This is the entry for black-preserving K-only intents, which are non-ICC
 679 static
 680 cmsPipeline*  BlackPreservingKOnlyIntents(cmsContext     ContextID,
 681                                           cmsUInt32Number nProfiles,
 682                                           cmsUInt32Number TheIntents[],
 683                                           cmsHPROFILE     hProfiles[],
 684                                           cmsBool         BPC[],
 685                                           cmsFloat64Number AdaptationStates[],
 686                                           cmsUInt32Number dwFlags)
 687 {
 688     GrayOnlyParams  bp;
 689     cmsPipeline*    Result;
 690     cmsUInt32Number ICCIntents[256];
 691     cmsStage*         CLUT;
 692     cmsUInt32Number i, nGridPoints;
 693 
 694 
 695     // Sanity check
 696     if (nProfiles < 1 || nProfiles > 255) return NULL;
 697 
 698     // Translate black-preserving intents to ICC ones
 699     for (i=0; i < nProfiles; i++)
 700         ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]);
 701 
 702     // Check for non-cmyk profiles
 703     if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||
 704         cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigCmykData)
 705            return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags);
 706 
 707     memset(&bp, 0, sizeof(bp));
 708 
 709     // Allocate an empty LUT for holding the result
 710     Result = cmsPipelineAlloc(ContextID, 4, 4);
 711     if (Result == NULL) return NULL;
 712 
 713     // Create a LUT holding normal ICC transform
 714     bp.cmyk2cmyk = DefaultICCintents(ContextID,
 715         nProfiles,
 716         ICCIntents,
 717         hProfiles,
 718         BPC,
 719         AdaptationStates,
 720         dwFlags);
 721 
 722     if (bp.cmyk2cmyk == NULL) goto Error;
 723 
 724     // Now, compute the tone curve
 725     bp.KTone = _cmsBuildKToneCurve(ContextID,
 726         4096,
 727         nProfiles,
 728         ICCIntents,
 729         hProfiles,
 730         BPC,
 731         AdaptationStates,
 732         dwFlags);
 733 
 734     if (bp.KTone == NULL) goto Error;
 735 
 736 
 737     // How many gridpoints are we going to use?
 738     nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags);
 739 
 740     // Create the CLUT. 16 bits
 741     CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL);
 742     if (CLUT == NULL) goto Error;
 743 
 744     // This is the one and only MPE in this LUT
 745     cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT);
 746 
 747     // Sample it. We cannot afford pre/post linearization this time.
 748     if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0))
 749         goto Error;
 750 
 751     // Get rid of xform and tone curve
 752     cmsPipelineFree(bp.cmyk2cmyk);
 753     cmsFreeToneCurve(bp.KTone);
 754 
 755     return Result;
 756 
 757 Error:
 758 
 759     if (bp.cmyk2cmyk != NULL) cmsPipelineFree(bp.cmyk2cmyk);
 760     if (bp.KTone != NULL)  cmsFreeToneCurve(bp.KTone);
 761     if (Result != NULL) cmsPipelineFree(Result);
 762     return NULL;
 763 
 764 }
 765 
 766 // K Plane-preserving CMYK to CMYK ------------------------------------------------------------------------------------
 767 
 768 typedef struct {
 769 
 770     cmsPipeline*     cmyk2cmyk;     // The original transform
 771     cmsHTRANSFORM    hProofOutput;  // Output CMYK to Lab (last profile)
 772     cmsHTRANSFORM    cmyk2Lab;      // The input chain
 773     cmsToneCurve*    KTone;         // Black-to-black tone curve
 774     cmsPipeline*     LabK2cmyk;     // The output profile
 775     cmsFloat64Number MaxError;
 776 
 777     cmsHTRANSFORM    hRoundTrip;
 778     cmsFloat64Number MaxTAC;
 779 
 780 
 781 } PreserveKPlaneParams;
 782 
 783 
 784 // The CLUT will be stored at 16 bits, but calculations are performed at cmsFloat32Number precision
 785 static
 786 int BlackPreservingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
 787 {
 788     int i;
 789     cmsFloat32Number Inf[4], Outf[4];
 790     cmsFloat32Number LabK[4];
 791     cmsFloat64Number SumCMY, SumCMYK, Error, Ratio;
 792     cmsCIELab ColorimetricLab, BlackPreservingLab;
 793     PreserveKPlaneParams* bp = (PreserveKPlaneParams*) Cargo;
 794 
 795     // Convert from 16 bits to floating point
 796     for (i=0; i < 4; i++)
 797         Inf[i] = (cmsFloat32Number) (In[i] / 65535.0);
 798 
 799     // Get the K across Tone curve
 800     LabK[3] = cmsEvalToneCurveFloat(bp ->KTone, Inf[3]);
 801 
 802     // If going across black only, keep black only
 803     if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
 804 
 805         Out[0] = Out[1] = Out[2] = 0;
 806         Out[3] = _cmsQuickSaturateWord(LabK[3] * 65535.0);
 807         return TRUE;
 808     }
 809 
 810     // Try the original transform,
 811     cmsPipelineEvalFloat( Inf, Outf, bp ->cmyk2cmyk);
 812 
 813     // Store a copy of the floating point result into 16-bit
 814     for (i=0; i < 4; i++)
 815             Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0);
 816 
 817     // Maybe K is already ok (mostly on K=0)
 818     if ( fabs(Outf[3] - LabK[3]) < (3.0 / 65535.0) ) {
 819         return TRUE;
 820     }
 821 
 822     // K differ, mesure and keep Lab measurement for further usage
 823     // this is done in relative colorimetric intent
 824     cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);
 825 
 826     // Is not black only and the transform doesn't keep black.
 827     // Obtain the Lab of output CMYK. After that we have Lab + K
 828     cmsDoTransform(bp ->cmyk2Lab, Outf, LabK, 1);
 829 
 830     // Obtain the corresponding CMY using reverse interpolation
 831     // (K is fixed in LabK[3])
 832     if (!cmsPipelineEvalReverseFloat(LabK, Outf, Outf, bp ->LabK2cmyk)) {
 833 
 834         // Cannot find a suitable value, so use colorimetric xform
 835         // which is already stored in Out[]
 836         return TRUE;
 837     }
 838 
 839     // Make sure to pass thru K (which now is fixed)
 840     Outf[3] = LabK[3];
 841 
 842     // Apply TAC if needed
 843     SumCMY   = Outf[0]  + Outf[1] + Outf[2];
 844     SumCMYK  = SumCMY + Outf[3];
 845 
 846     if (SumCMYK > bp ->MaxTAC) {
 847 
 848         Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY);
 849         if (Ratio < 0)
 850             Ratio = 0;
 851     }
 852     else
 853        Ratio = 1.0;
 854 
 855     Out[0] = _cmsQuickSaturateWord(Outf[0] * Ratio * 65535.0);     // C
 856     Out[1] = _cmsQuickSaturateWord(Outf[1] * Ratio * 65535.0);     // M
 857     Out[2] = _cmsQuickSaturateWord(Outf[2] * Ratio * 65535.0);     // Y
 858     Out[3] = _cmsQuickSaturateWord(Outf[3] * 65535.0);
 859 
 860     // Estimate the error (this goes 16 bits to Lab DBL)
 861     cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
 862     Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
 863     if (Error > bp -> MaxError)
 864         bp->MaxError = Error;
 865 
 866     return TRUE;
 867 }
 868 
 869 // This is the entry for black-plane preserving, which are non-ICC
 870 static
 871 cmsPipeline* BlackPreservingKPlaneIntents(cmsContext     ContextID,
 872                                           cmsUInt32Number nProfiles,
 873                                           cmsUInt32Number TheIntents[],
 874                                           cmsHPROFILE     hProfiles[],
 875                                           cmsBool         BPC[],
 876                                           cmsFloat64Number AdaptationStates[],
 877                                           cmsUInt32Number dwFlags)
 878 {
 879     PreserveKPlaneParams bp;
 880     cmsPipeline*    Result = NULL;
 881     cmsUInt32Number ICCIntents[256];
 882     cmsStage*         CLUT;
 883     cmsUInt32Number i, nGridPoints;
 884     cmsHPROFILE hLab;
 885 
 886     // Sanity check
 887     if (nProfiles < 1 || nProfiles > 255) return NULL;
 888 
 889     // Translate black-preserving intents to ICC ones
 890     for (i=0; i < nProfiles; i++)
 891         ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]);
 892 
 893     // Check for non-cmyk profiles
 894     if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||
 895         !(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData ||
 896         cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass))
 897            return  DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags);
 898 
 899     // Allocate an empty LUT for holding the result
 900     Result = cmsPipelineAlloc(ContextID, 4, 4);
 901     if (Result == NULL) return NULL;
 902 
 903 
 904     memset(&bp, 0, sizeof(bp));
 905 
 906     // We need the input LUT of the last profile, assuming this one is responsible of
 907     // black generation. This LUT will be seached in inverse order.
 908     bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC);
 909     if (bp.LabK2cmyk == NULL) goto Cleanup;
 910 
 911     // Get total area coverage (in 0..1 domain)
 912     bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0;
 913     if (bp.MaxTAC <= 0) goto Cleanup;
 914 
 915 
 916     // Create a LUT holding normal ICC transform
 917     bp.cmyk2cmyk = DefaultICCintents(ContextID,
 918                                          nProfiles,
 919                                          ICCIntents,
 920                                          hProfiles,
 921                                          BPC,
 922                                          AdaptationStates,
 923                                          dwFlags);
 924     if (bp.cmyk2cmyk == NULL) goto Cleanup;
 925 
 926     // Now the tone curve
 927     bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles,
 928                                    ICCIntents,
 929                                    hProfiles,
 930                                    BPC,
 931                                    AdaptationStates,
 932                                    dwFlags);
 933     if (bp.KTone == NULL) goto Cleanup;
 934 
 935     // To measure the output, Last profile to Lab
 936     hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
 937     bp.hProofOutput = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1],
 938                                          CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL,
 939                                          INTENT_RELATIVE_COLORIMETRIC,
 940                                          cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
 941     if ( bp.hProofOutput == NULL) goto Cleanup;
 942 
 943     // Same as anterior, but lab in the 0..1 range
 944     bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1],
 945                                          FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab,
 946                                          FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4),
 947                                          INTENT_RELATIVE_COLORIMETRIC,
 948                                          cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
 949     if (bp.cmyk2Lab == NULL) goto Cleanup;
 950     cmsCloseProfile(hLab);
 951 
 952     // Error estimation (for debug only)
 953     bp.MaxError = 0;
 954 
 955     // How many gridpoints are we going to use?
 956     nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags);
 957 
 958 
 959     CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL);
 960     if (CLUT == NULL) goto Cleanup;
 961 
 962     cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT);
 963 
 964     cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0);
 965 
 966 Cleanup:
 967 
 968     if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk);
 969     if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab);
 970     if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput);
 971 
 972     if (bp.KTone) cmsFreeToneCurve(bp.KTone);
 973     if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk);
 974 
 975     return Result;
 976 }
 977 
 978 // Link routines ------------------------------------------------------------------------------------------------------
 979 
 980 // Chain several profiles into a single LUT. It just checks the parameters and then calls the handler
 981 // for the first intent in chain. The handler may be user-defined. Is up to the handler to deal with the
 982 // rest of intents in chain. A maximum of 255 profiles at time are supported, which is pretty reasonable.
 983 cmsPipeline* _cmsLinkProfiles(cmsContext     ContextID,
 984                               cmsUInt32Number nProfiles,
 985                               cmsUInt32Number TheIntents[],
 986                               cmsHPROFILE     hProfiles[],
 987                               cmsBool         BPC[],
 988                               cmsFloat64Number AdaptationStates[],
 989                               cmsUInt32Number dwFlags)
 990 {
 991     cmsUInt32Number i;
 992     cmsIntentsList* Intent;
 993 
 994     // Make sure a reasonable number of profiles is provided
 995     if (nProfiles <= 0 || nProfiles > 255) {
 996          cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't link '%d' profiles", nProfiles);
 997         return NULL;
 998     }
 999 
1000     for (i=0; i < nProfiles; i++) {
1001 
1002         // Check if black point is really needed or allowed. Note that
1003         // following Adobe's document:
1004         // BPC does not apply to devicelink profiles, nor to abs colorimetric,
1005         // and applies always on V4 perceptual and saturation.
1006 
1007         if (TheIntents[i] == INTENT_ABSOLUTE_COLORIMETRIC)
1008             BPC[i] = FALSE;
1009 
1010         if (TheIntents[i] == INTENT_PERCEPTUAL || TheIntents[i] == INTENT_SATURATION) {
1011 
1012             // Force BPC for V4 profiles in perceptual and saturation
1013             if (cmsGetProfileVersion(hProfiles[i]) >= 4.0)
1014                 BPC[i] = TRUE;
1015         }
1016     }
1017 
1018     // Search for a handler. The first intent in the chain defines the handler. That would
1019     // prevent using multiple custom intents in a multiintent chain, but the behaviour of
1020     // this case would present some issues if the custom intent tries to do things like
1021     // preserve primaries. This solution is not perfect, but works well on most cases.
1022 
1023     Intent = SearchIntent(TheIntents[0]);
1024     if (Intent == NULL) {
1025         cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]);
1026         return NULL;
1027     }
1028 
1029     // Call the handler
1030     return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags);
1031 }
1032 
1033 // -------------------------------------------------------------------------------------------------
1034 
1035 // Get information about available intents. nMax is the maximum space for the supplied "Codes"
1036 // and "Descriptions" the function returns the total number of intents, which may be greater
1037 // than nMax, although the matrices are not populated beyond this level.
1038 cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions)
1039 {
1040     cmsIntentsList* pt;
1041     cmsUInt32Number nIntents;
1042 
1043     for (nIntents=0, pt = Intents; pt != NULL; pt = pt -> Next)
1044     {
1045         if (nIntents < nMax) {
1046             if (Codes != NULL)
1047                 Codes[nIntents] = pt ->Intent;
1048 
1049             if (Descriptions != NULL)
1050                 Descriptions[nIntents] = pt ->Description;
1051         }
1052 
1053         nIntents++;
1054     }
1055 
1056     return nIntents;
1057 }
1058 
1059 // The plug-in registration. User can add new intents or override default routines
1060 cmsBool  _cmsRegisterRenderingIntentPlugin(cmsPluginBase* Data)
1061 {
1062     cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data;
1063     cmsIntentsList* fl;
1064 
1065     // Do we have to reset the intents?
1066     if (Data == NULL) {
1067 
1068        Intents = DefaultIntents;
1069        return TRUE;
1070     }
1071 
1072     fl = SearchIntent(Plugin ->Intent);
1073 
1074     if (fl == NULL) {
1075         fl = (cmsIntentsList*) _cmsPluginMalloc(sizeof(cmsIntentsList));
1076         if (fl == NULL) return FALSE;
1077     }
1078 
1079     fl ->Intent  = Plugin ->Intent;
1080     strncpy(fl ->Description, Plugin ->Description, 255);
1081     fl ->Description[255] = 0;
1082 
1083     fl ->Link    = Plugin ->Link;
1084 
1085     fl ->Next = Intents;
1086     Intents = fl;
1087 
1088     return TRUE;
1089 }
1090