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-2011 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 // PostScript ColorRenderingDictionary and ColorSpaceArray
  59 
  60 
  61 #define MAXPSCOLS   60      // Columns on tables
  62 
  63 /*
  64     Implementation
  65     --------------
  66 
  67   PostScript does use XYZ as its internal PCS. But since PostScript
  68   interpolation tables are limited to 8 bits, I use Lab as a way to
  69   improve the accuracy, favoring perceptual results. So, for the creation
  70   of each CRD, CSA the profiles are converted to Lab via a device
  71   link between  profile -> Lab or Lab -> profile. The PS code necessary to
  72   convert Lab <-> XYZ is also included.
  73 
  74 
  75 
  76   Color Space Arrays (CSA)
  77   ==================================================================================
  78 
  79   In order to obtain precision, code chooses between three ways to implement
  80   the device -> XYZ transform. These cases identifies monochrome profiles (often
  81   implemented as a set of curves), matrix-shaper and Pipeline-based.
  82 
  83   Monochrome
  84   -----------
  85 
  86   This is implemented as /CIEBasedA CSA. The prelinearization curve is
  87   placed into /DecodeA section, and matrix equals to D50. Since here is
  88   no interpolation tables, I do the conversion directly to XYZ
  89 
  90   NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT
  91   flag is forced on such profiles.
  92 
  93     [ /CIEBasedA
  94       <<
  95             /DecodeA { transfer function } bind
  96             /MatrixA [D50]
  97             /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]
  98             /WhitePoint [D50]
  99             /BlackPoint [BP]
 100             /RenderingIntent (intent)
 101       >>
 102     ]
 103 
 104    On simpler profiles, the PCS is already XYZ, so no conversion is required.
 105 
 106 
 107    Matrix-shaper based
 108    -------------------
 109 
 110    This is implemented both with /CIEBasedABC or /CIEBasedDEF on dependig
 111    of profile implementation. Since here there are no interpolation tables, I do
 112    the conversion directly to XYZ
 113 
 114 
 115 
 116     [ /CIEBasedABC
 117             <<
 118                 /DecodeABC [ {transfer1} {transfer2} {transfer3} ]
 119                 /MatrixABC [Matrix]
 120                 /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]
 121                 /DecodeLMN [ { / 2} dup dup ]
 122                 /WhitePoint [D50]
 123                 /BlackPoint [BP]
 124                 /RenderingIntent (intent)
 125             >>
 126     ]
 127 
 128 
 129     CLUT based
 130     ----------
 131 
 132      Lab is used in such cases.
 133 
 134     [ /CIEBasedDEF
 135             <<
 136             /DecodeDEF [ <prelinearization> ]
 137             /Table [ p p p [<...>]]
 138             /RangeABC [ 0 1 0 1 0 1]
 139             /DecodeABC[ <postlinearization> ]
 140             /RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]
 141                % -128/500 1+127/500 0 1  -127/200 1+128/200
 142             /MatrixABC [ 1 1 1 1 0 0 0 0 -1]
 143             /WhitePoint [D50]
 144             /BlackPoint [BP]
 145             /RenderingIntent (intent)
 146     ]
 147 
 148 
 149   Color Rendering Dictionaries (CRD)
 150   ==================================
 151   These are always implemented as CLUT, and always are using Lab. Since CRD are expected to
 152   be used as resources, the code adds the definition as well.
 153 
 154   <<
 155     /ColorRenderingType 1
 156     /WhitePoint [ D50 ]
 157     /BlackPoint [BP]
 158     /MatrixPQR [ Bradford ]
 159     /RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ]
 160     /TransformPQR [
 161     {4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind
 162     {4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind
 163     {4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind
 164     ]
 165     /MatrixABC <...>
 166     /EncodeABC <...>
 167     /RangeABC  <.. used for  XYZ -> Lab>
 168     /EncodeLMN
 169     /RenderTable [ p p p [<...>]]
 170 
 171     /RenderingIntent (Perceptual)
 172   >>
 173   /Current exch /ColorRendering defineresource pop
 174 
 175 
 176   The following stages are used to convert from XYZ to Lab
 177   --------------------------------------------------------
 178 
 179   Input is given at LMN stage on X, Y, Z
 180 
 181   Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn)
 182 
 183   /EncodeLMN [
 184 
 185     { 0.964200  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
 186     { 1.000000  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
 187     { 0.824900  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
 188 
 189     ]
 190 
 191 
 192   MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn)
 193 
 194   | 0  1  0|
 195   | 1 -1  0|
 196   | 0  1 -1|
 197 
 198   /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]
 199 
 200  EncodeABC finally gives Lab values.
 201 
 202   /EncodeABC [
 203     { 116 mul  16 sub 100 div  } bind
 204     { 500 mul 128 add 255 div  } bind
 205     { 200 mul 128 add 255 div  } bind
 206     ]
 207 
 208   The following stages are used to convert Lab to XYZ
 209   ----------------------------------------------------
 210 
 211     /RangeABC [ 0 1 0 1 0 1]
 212     /DecodeABC [ { 100 mul 16 add 116 div } bind
 213                  { 255 mul 128 sub 500 div } bind
 214                  { 255 mul 128 sub 200 div } bind
 215                ]
 216 
 217     /MatrixABC [ 1 1 1 1 0 0 0 0 -1]
 218     /DecodeLMN [
 219                 {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind
 220                 {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind
 221                 {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind
 222                 ]
 223 
 224 
 225 */
 226 
 227 /*
 228 
 229  PostScript algorithms discussion.
 230  =========================================================================================================
 231 
 232   1D interpolation algorithm
 233 
 234 
 235   1D interpolation (float)
 236   ------------------------
 237 
 238     val2 = Domain * Value;
 239 
 240     cell0 = (int) floor(val2);
 241     cell1 = (int) ceil(val2);
 242 
 243     rest = val2 - cell0;
 244 
 245     y0 = LutTable[cell0] ;
 246     y1 = LutTable[cell1] ;
 247 
 248     y = y0 + (y1 - y0) * rest;
 249 
 250 
 251 
 252   PostScript code                   Stack
 253   ================================================
 254 
 255   {                                 % v
 256     <check 0..1.0>
 257     [array]                         % v tab
 258     dup                             % v tab tab
 259     length 1 sub                    % v tab dom
 260 
 261     3 -1 roll                       % tab dom v
 262 
 263     mul                             % tab val2
 264     dup                             % tab val2 val2
 265     dup                             % tab val2 val2 val2
 266     floor cvi                       % tab val2 val2 cell0
 267     exch                            % tab val2 cell0 val2
 268     ceiling cvi                     % tab val2 cell0 cell1
 269 
 270     3 index                         % tab val2 cell0 cell1 tab
 271     exch                            % tab val2 cell0 tab cell1
 272     get                             % tab val2 cell0 y1
 273 
 274     4 -1 roll                       % val2 cell0 y1 tab
 275     3 -1 roll                       % val2 y1 tab cell0
 276     get                             % val2 y1 y0
 277 
 278     dup                             % val2 y1 y0 y0
 279     3 1 roll                        % val2 y0 y1 y0
 280 
 281     sub                             % val2 y0 (y1-y0)
 282     3 -1 roll                       % y0 (y1-y0) val2
 283     dup                             % y0 (y1-y0) val2 val2
 284     floor cvi                       % y0 (y1-y0) val2 floor(val2)
 285     sub                             % y0 (y1-y0) rest
 286     mul                             % y0 t1
 287     add                             % y
 288     65535 div                       % result
 289 
 290   } bind
 291 
 292 
 293 */
 294 
 295 
 296 // This struct holds the memory block currently being write
 297 typedef struct {
 298     _cmsStageCLutData* Pipeline;
 299     cmsIOHANDLER* m;
 300 
 301     int FirstComponent;
 302     int SecondComponent;
 303 
 304     const char* PreMaj;
 305     const char* PostMaj;
 306     const char* PreMin;
 307     const char* PostMin;
 308 
 309     int  FixWhite;    // Force mapping of pure white
 310 
 311     cmsColorSpaceSignature  ColorSpace;  // ColorSpace of profile
 312 
 313 
 314 } cmsPsSamplerCargo;
 315 
 316 static int _cmsPSActualColumn = 0;
 317 
 318 
 319 // Convert to byte
 320 static
 321 cmsUInt8Number Word2Byte(cmsUInt16Number w)
 322 {
 323     return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5);
 324 }
 325 
 326 
 327 // Convert to byte (using ICC2 notation)
 328 /*
 329 static
 330 cmsUInt8Number L2Byte(cmsUInt16Number w)
 331 {
 332     int ww = w + 0x0080;
 333 
 334     if (ww > 0xFFFF) return 0xFF;
 335 
 336     return (cmsUInt8Number) ((cmsUInt16Number) (ww >> 8) & 0xFF);
 337 }
 338 */
 339 
 340 // Write a cooked byte
 341 
 342 static
 343 void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b)
 344 {
 345     _cmsIOPrintf(m, "%02x", b);
 346     _cmsPSActualColumn += 2;
 347 
 348     if (_cmsPSActualColumn > MAXPSCOLS) {
 349 
 350         _cmsIOPrintf(m, "\n");
 351         _cmsPSActualColumn = 0;
 352     }
 353 }
 354 
 355 // ----------------------------------------------------------------- PostScript generation
 356 
 357 
 358 // Removes offending Carriage returns
 359 static
 360 char* RemoveCR(const char* txt)
 361 {
 362     static char Buffer[2048];
 363     char* pt;
 364 
 365     strncpy(Buffer, txt, 2047);
 366     Buffer[2047] = 0;
 367     for (pt = Buffer; *pt; pt++)
 368             if (*pt == '\n' || *pt == '\r') *pt = ' ';
 369 
 370     return Buffer;
 371 
 372 }
 373 
 374 static
 375 void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile)
 376 {
 377     time_t timer;
 378     cmsMLU *Description, *Copyright;
 379     char DescASCII[256], CopyrightASCII[256];
 380 
 381     time(&timer);
 382 
 383     Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag);
 384     Copyright   = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag);
 385 
 386     DescASCII[0] = DescASCII[255] = 0;
 387     CopyrightASCII[0] = CopyrightASCII[255] = 0;
 388 
 389     if (Description != NULL) cmsMLUgetASCII(Description,  cmsNoLanguage, cmsNoCountry, DescASCII,       255);
 390     if (Copyright != NULL)   cmsMLUgetASCII(Copyright,    cmsNoLanguage, cmsNoCountry, CopyrightASCII,  255);
 391 
 392     _cmsIOPrintf(m, "%%!PS-Adobe-3.0\n");
 393     _cmsIOPrintf(m, "%%\n");
 394     _cmsIOPrintf(m, "%% %s\n", Title);
 395     _cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII));
 396     _cmsIOPrintf(m, "%%         %s\n", RemoveCR(CopyrightASCII));
 397     _cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!!
 398     _cmsIOPrintf(m, "%%\n");
 399     _cmsIOPrintf(m, "%%%%BeginResource\n");
 400 
 401 }
 402 
 403 
 404 // Emits White & Black point. White point is always D50, Black point is the device
 405 // Black point adapted to D50.
 406 
 407 static
 408 void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint)
 409 {
 410 
 411     _cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X,
 412                                           BlackPoint -> Y,
 413                                           BlackPoint -> Z);
 414 
 415     _cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X,
 416                                           cmsD50_XYZ()->Y,
 417                                           cmsD50_XYZ()->Z);
 418 }
 419 
 420 
 421 static
 422 void EmitRangeCheck(cmsIOHANDLER* m)
 423 {
 424     _cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if "
 425                     "dup 1.0 gt { pop 1.0 } if ");
 426 
 427 }
 428 
 429 // Does write the intent
 430 
 431 static
 432 void EmitIntent(cmsIOHANDLER* m, int RenderingIntent)
 433 {
 434     const char *intent;
 435 
 436     switch (RenderingIntent) {
 437 
 438         case INTENT_PERCEPTUAL:            intent = "Perceptual"; break;
 439         case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break;
 440         case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break;
 441         case INTENT_SATURATION:            intent = "Saturation"; break;
 442 
 443         default: intent = "Undefined"; break;
 444     }
 445 
 446     _cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent );
 447 }
 448 
 449 //
 450 //  Convert L* to Y
 451 //
 452 //      Y = Yn*[ (L* + 16) / 116] ^ 3   if (L*) >= 6 / 29
 453 //        = Yn*( L* / 116) / 7.787      if (L*) < 6 / 29
 454 //
 455 
 456 /*
 457 static
 458 void EmitL2Y(cmsIOHANDLER* m)
 459 {
 460     _cmsIOPrintf(m,
 461             "{ "
 462                 "100 mul 16 add 116 div "               // (L * 100 + 16) / 116
 463                  "dup 6 29 div ge "                     // >= 6 / 29 ?
 464                  "{ dup dup mul mul } "                 // yes, ^3 and done
 465                  "{ 4 29 div sub 108 841 div mul } "    // no, slope limiting
 466             "ifelse } bind ");
 467 }
 468 */
 469 
 470 
 471 // Lab -> XYZ, see the discussion above
 472 
 473 static
 474 void EmitLab2XYZ(cmsIOHANDLER* m)
 475 {
 476     _cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n");
 477     _cmsIOPrintf(m, "/DecodeABC [\n");
 478     _cmsIOPrintf(m, "{100 mul  16 add 116 div } bind\n");
 479     _cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n");
 480     _cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n");
 481     _cmsIOPrintf(m, "]\n");
 482     _cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n");
 483     _cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n");
 484     _cmsIOPrintf(m, "/DecodeLMN [\n");
 485     _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n");
 486     _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n");
 487     _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n");
 488     _cmsIOPrintf(m, "]\n");
 489 }
 490 
 491 
 492 
 493 // Outputs a table of words. It does use 16 bits
 494 
 495 static
 496 void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
 497 {
 498     cmsUInt32Number i;
 499     cmsFloat64Number gamma;
 500 
 501     if (Table == NULL) return; // Error
 502 
 503     if (Table ->nEntries <= 0) return;  // Empty table
 504 
 505     // Suppress whole if identity
 506     if (cmsIsToneCurveLinear(Table)) return;
 507 
 508     // Check if is really an exponential. If so, emit "exp"
 509     gamma = cmsEstimateGamma(Table, 0.001);
 510      if (gamma > 0) {
 511             _cmsIOPrintf(m, "{ %g exp } bind ", gamma);
 512             return;
 513      }
 514 
 515     _cmsIOPrintf(m, "{ ");
 516 
 517     // Bounds check
 518     EmitRangeCheck(m);
 519 
 520     // Emit intepolation code
 521 
 522     // PostScript code                      Stack
 523     // ===============                      ========================
 524                                             // v
 525     _cmsIOPrintf(m, " [");
 526 
 527     for (i=0; i < Table->nEntries; i++) {
 528         _cmsIOPrintf(m, "%d ", Table->Table16[i]);
 529     }
 530 
 531     _cmsIOPrintf(m, "] ");                        // v tab
 532 
 533     _cmsIOPrintf(m, "dup ");                      // v tab tab
 534     _cmsIOPrintf(m, "length 1 sub ");             // v tab dom
 535     _cmsIOPrintf(m, "3 -1 roll ");                // tab dom v
 536     _cmsIOPrintf(m, "mul ");                      // tab val2
 537     _cmsIOPrintf(m, "dup ");                      // tab val2 val2
 538     _cmsIOPrintf(m, "dup ");                      // tab val2 val2 val2
 539     _cmsIOPrintf(m, "floor cvi ");                // tab val2 val2 cell0
 540     _cmsIOPrintf(m, "exch ");                     // tab val2 cell0 val2
 541     _cmsIOPrintf(m, "ceiling cvi ");              // tab val2 cell0 cell1
 542     _cmsIOPrintf(m, "3 index ");                  // tab val2 cell0 cell1 tab
 543     _cmsIOPrintf(m, "exch ");                     // tab val2 cell0 tab cell1
 544     _cmsIOPrintf(m, "get ");                      // tab val2 cell0 y1
 545     _cmsIOPrintf(m, "4 -1 roll ");                // val2 cell0 y1 tab
 546     _cmsIOPrintf(m, "3 -1 roll ");                // val2 y1 tab cell0
 547     _cmsIOPrintf(m, "get ");                      // val2 y1 y0
 548     _cmsIOPrintf(m, "dup ");                      // val2 y1 y0 y0
 549     _cmsIOPrintf(m, "3 1 roll ");                 // val2 y0 y1 y0
 550     _cmsIOPrintf(m, "sub ");                      // val2 y0 (y1-y0)
 551     _cmsIOPrintf(m, "3 -1 roll ");                // y0 (y1-y0) val2
 552     _cmsIOPrintf(m, "dup ");                      // y0 (y1-y0) val2 val2
 553     _cmsIOPrintf(m, "floor cvi ");                // y0 (y1-y0) val2 floor(val2)
 554     _cmsIOPrintf(m, "sub ");                      // y0 (y1-y0) rest
 555     _cmsIOPrintf(m, "mul ");                      // y0 t1
 556     _cmsIOPrintf(m, "add ");                      // y
 557     _cmsIOPrintf(m, "65535 div ");                // result
 558 
 559     _cmsIOPrintf(m, " } bind ");
 560 }
 561 
 562 
 563 // Compare gamma table
 564 
 565 static
 566 cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, int nEntries)
 567 {
 568     return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0;
 569 }
 570 
 571 
 572 // Does write a set of gamma curves
 573 
 574 static
 575 void EmitNGamma(cmsIOHANDLER* m, int n, cmsToneCurve* g[])
 576 {
 577     int i;
 578 
 579     for( i=0; i < n; i++ )
 580     {
 581         if (g[i] == NULL) return; // Error
 582 
 583         if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) {
 584 
 585             _cmsIOPrintf(m, "dup ");
 586         }
 587         else {
 588             Emit1Gamma(m, g[i]);
 589         }
 590     }
 591 
 592 }
 593 
 594 
 595 
 596 
 597 
 598 // Following code dumps a LUT onto memory stream
 599 
 600 
 601 // This is the sampler. Intended to work in SAMPLER_INSPECT mode,
 602 // that is, the callback will be called for each knot with
 603 //
 604 //          In[]  The grid location coordinates, normalized to 0..ffff
 605 //          Out[] The Pipeline values, normalized to 0..ffff
 606 //
 607 //  Returning a value other than 0 does terminate the sampling process
 608 //
 609 //  Each row contains Pipeline values for all but first component. So, I
 610 //  detect row changing by keeping a copy of last value of first
 611 //  component. -1 is used to mark begining of whole block.
 612 
 613 static
 614 int OutputValueSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
 615 {
 616     cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;
 617     cmsUInt32Number i;
 618 
 619 
 620     if (sc -> FixWhite) {
 621 
 622         if (In[0] == 0xFFFF) {  // Only in L* = 100, ab = [-8..8]
 623 
 624             if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&
 625                 (In[2] >= 0x7800 && In[2] <= 0x8800)) {
 626 
 627                 cmsUInt16Number* Black;
 628                 cmsUInt16Number* White;
 629                 cmsUInt32Number nOutputs;
 630 
 631                 if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))
 632                         return 0;
 633 
 634                 for (i=0; i < nOutputs; i++)
 635                         Out[i] = White[i];
 636             }
 637 
 638 
 639         }
 640     }
 641 
 642 
 643     // Hadle the parenthesis on rows
 644 
 645     if (In[0] != sc ->FirstComponent) {
 646 
 647             if (sc ->FirstComponent != -1) {
 648 
 649                     _cmsIOPrintf(sc ->m, sc ->PostMin);
 650                     sc ->SecondComponent = -1;
 651                     _cmsIOPrintf(sc ->m, sc ->PostMaj);
 652             }
 653 
 654             // Begin block
 655             _cmsPSActualColumn = 0;
 656 
 657             _cmsIOPrintf(sc ->m, sc ->PreMaj);
 658             sc ->FirstComponent = In[0];
 659     }
 660 
 661 
 662       if (In[1] != sc ->SecondComponent) {
 663 
 664             if (sc ->SecondComponent != -1) {
 665 
 666                     _cmsIOPrintf(sc ->m, sc ->PostMin);
 667             }
 668 
 669             _cmsIOPrintf(sc ->m, sc ->PreMin);
 670             sc ->SecondComponent = In[1];
 671     }
 672 
 673       // Dump table.
 674 
 675       for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {
 676 
 677           cmsUInt16Number wWordOut = Out[i];
 678           cmsUInt8Number wByteOut;           // Value as byte
 679 
 680 
 681           // We always deal with Lab4
 682 
 683           wByteOut = Word2Byte(wWordOut);
 684           WriteByte(sc -> m, wByteOut);
 685       }
 686 
 687       return 1;
 688 }
 689 
 690 // Writes a Pipeline on memstream. Could be 8 or 16 bits based
 691 
 692 static
 693 void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
 694                                              const char* PostMaj,
 695                                              const char* PreMin,
 696                                              const char* PostMin,
 697                                              int FixWhite,
 698                                              cmsColorSpaceSignature ColorSpace)
 699 {
 700     cmsUInt32Number i;
 701     cmsPsSamplerCargo sc;
 702 
 703     sc.FirstComponent = -1;
 704     sc.SecondComponent = -1;
 705     sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
 706     sc.m   = m;
 707     sc.PreMaj = PreMaj;
 708     sc.PostMaj= PostMaj;
 709 
 710     sc.PreMin   = PreMin;
 711     sc.PostMin  = PostMin;
 712     sc.FixWhite = FixWhite;
 713     sc.ColorSpace = ColorSpace;
 714 
 715     _cmsIOPrintf(m, "[");
 716 
 717     for (i=0; i < sc.Pipeline->Params->nInputs; i++)
 718         _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
 719 
 720     _cmsIOPrintf(m, " [\n");
 721 
 722     cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT);
 723 
 724     _cmsIOPrintf(m, PostMin);
 725     _cmsIOPrintf(m, PostMaj);
 726     _cmsIOPrintf(m, "] ");
 727 
 728 }
 729 
 730 
 731 // Dumps CIEBasedA Color Space Array
 732 
 733 static
 734 int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
 735 {
 736 
 737     _cmsIOPrintf(m, "[ /CIEBasedA\n");
 738     _cmsIOPrintf(m, "  <<\n");
 739 
 740     _cmsIOPrintf(m, "/DecodeA ");
 741 
 742     Emit1Gamma(m, Curve);
 743 
 744     _cmsIOPrintf(m, " \n");
 745 
 746     _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
 747     _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
 748 
 749     EmitWhiteBlackD50(m, BlackPoint);
 750     EmitIntent(m, INTENT_PERCEPTUAL);
 751 
 752     _cmsIOPrintf(m, ">>\n");
 753     _cmsIOPrintf(m, "]\n");
 754 
 755     return 1;
 756 }
 757 
 758 
 759 // Dumps CIEBasedABC Color Space Array
 760 
 761 static
 762 int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)
 763 {
 764     int i;
 765 
 766     _cmsIOPrintf(m, "[ /CIEBasedABC\n");
 767     _cmsIOPrintf(m, "<<\n");
 768     _cmsIOPrintf(m, "/DecodeABC [ ");
 769 
 770     EmitNGamma(m, 3, CurveSet);
 771 
 772     _cmsIOPrintf(m, "]\n");
 773 
 774     _cmsIOPrintf(m, "/MatrixABC [ " );
 775 
 776     for( i=0; i < 3; i++ ) {
 777 
 778         _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],
 779                                            Matrix[i + 3*1],
 780                                            Matrix[i + 3*2]);
 781     }
 782 
 783 
 784     _cmsIOPrintf(m, "]\n");
 785 
 786     _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
 787 
 788     EmitWhiteBlackD50(m, BlackPoint);
 789     EmitIntent(m, INTENT_PERCEPTUAL);
 790 
 791     _cmsIOPrintf(m, ">>\n");
 792     _cmsIOPrintf(m, "]\n");
 793 
 794 
 795     return 1;
 796 }
 797 
 798 
 799 static
 800 int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXYZ* BlackPoint)
 801 {
 802     const char* PreMaj;
 803     const char* PostMaj;
 804     const char* PreMin, *PostMin;
 805     cmsStage* mpe;
 806 
 807     mpe = Pipeline ->Elements;
 808 
 809 
 810     switch (cmsStageInputChannels(mpe)) {
 811     case 3:
 812 
 813             _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
 814             PreMaj ="<";
 815             PostMaj= ">\n";
 816             PreMin = PostMin = "";
 817             break;
 818     case 4:
 819             _cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
 820             PreMaj = "[";
 821             PostMaj = "]\n";
 822             PreMin = "<";
 823             PostMin = ">\n";
 824             break;
 825     default:
 826             return 0;
 827 
 828     }
 829 
 830     _cmsIOPrintf(m, "<<\n");
 831 
 832     if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
 833 
 834         _cmsIOPrintf(m, "/DecodeDEF [ ");
 835         EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
 836         _cmsIOPrintf(m, "]\n");
 837 
 838         mpe = mpe ->Next;
 839     }
 840 
 841 
 842 
 843     if (cmsStageType(mpe) == cmsSigCLutElemType) {
 844 
 845             _cmsIOPrintf(m, "/Table ");
 846             WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0);
 847             _cmsIOPrintf(m, "]\n");
 848     }
 849 
 850     EmitLab2XYZ(m);
 851     EmitWhiteBlackD50(m, BlackPoint);
 852     EmitIntent(m, Intent);
 853 
 854     _cmsIOPrintf(m, "   >>\n");
 855     _cmsIOPrintf(m, "]\n");
 856 
 857 
 858     return 1;
 859 }
 860 
 861 // Generates a curve from a gray profile
 862 
 863 static
 864     cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
 865 {
 866     cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
 867     cmsHPROFILE hXYZ  = cmsCreateXYZProfile();
 868     cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
 869     int i;
 870 
 871     if (Out != NULL) {
 872         for (i=0; i < 256; i++) {
 873 
 874             cmsUInt8Number Gray = (cmsUInt8Number) i;
 875             cmsCIEXYZ XYZ;
 876 
 877             cmsDoTransform(xform, &Gray, &XYZ, 1);
 878 
 879             Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);
 880         }
 881     }
 882 
 883     cmsDeleteTransform(xform);
 884     cmsCloseProfile(hXYZ);
 885     return Out;
 886 }
 887 
 888 
 889 
 890 // Because PostScript has only 8 bits in /Table, we should use
 891 // a more perceptually uniform space... I do choose Lab.
 892 
 893 static
 894 int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
 895 {
 896     cmsHPROFILE hLab;
 897     cmsHTRANSFORM xform;
 898     cmsUInt32Number nChannels;
 899     cmsUInt32Number InputFormat;
 900     int rc;
 901     cmsHPROFILE Profiles[2];
 902     cmsCIEXYZ BlackPointAdaptedToD50;
 903 
 904     // Does create a device-link based transform.
 905     // The DeviceLink is next dumped as working CSA.
 906 
 907     InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
 908     nChannels   = T_CHANNELS(InputFormat);
 909 
 910 
 911     cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
 912 
 913     // Adjust output to Lab4
 914     hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
 915 
 916     Profiles[0] = hProfile;
 917     Profiles[1] = hLab;
 918 
 919     xform = cmsCreateMultiprofileTransform(Profiles, 2,  InputFormat, TYPE_Lab_DBL, Intent, 0);
 920     cmsCloseProfile(hLab);
 921 
 922     if (xform == NULL) {
 923 
 924         cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
 925         return 0;
 926     }
 927 
 928     // Only 1, 3 and 4 channels are allowed
 929 
 930     switch (nChannels) {
 931 
 932     case 1: {
 933             cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
 934             EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
 935             cmsFreeToneCurve(Gray2Y);
 936             }
 937             break;
 938 
 939     case 3:
 940     case 4: {
 941             cmsUInt32Number OutFrm = TYPE_Lab_16;
 942             cmsPipeline* DeviceLink;
 943             _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
 944 
 945             DeviceLink = cmsPipelineDup(v ->Lut);
 946             if (DeviceLink == NULL) return 0;
 947 
 948             dwFlags |= cmsFLAGS_FORCE_CLUT;
 949             _cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
 950 
 951             rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
 952             cmsPipelineFree(DeviceLink);
 953             }
 954             break;
 955 
 956     default:
 957 
 958         cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
 959         return 0;
 960     }
 961 
 962 
 963     cmsDeleteTransform(xform);
 964 
 965     return 1;
 966 }
 967 
 968 static
 969 cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)
 970 {
 971     _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
 972 
 973     return Data -> Double;
 974 }
 975 
 976 
 977 // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
 978 
 979 static
 980 int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
 981 {
 982     cmsColorSpaceSignature ColorSpace;
 983     int rc;
 984     cmsCIEXYZ BlackPointAdaptedToD50;
 985 
 986     ColorSpace = cmsGetColorSpace(hProfile);
 987 
 988     cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
 989 
 990     if (ColorSpace == cmsSigGrayData) {
 991 
 992         cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
 993         rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
 994 
 995     }
 996     else
 997         if (ColorSpace == cmsSigRgbData) {
 998 
 999             cmsMAT3 Mat;
1000             int i, j;
1001 
1002             memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
1003 
1004             for (i=0; i < 3; i++)
1005                 for (j=0; j < 3; j++)
1006                     Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
1007 
1008             rc = EmitCIEBasedABC(m,  (cmsFloat64Number *) &Mat,
1009                                 _cmsStageGetPtrToCurveSet(Shaper),
1010                                  &BlackPointAdaptedToD50);
1011         }
1012         else  {
1013 
1014             cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
1015             return 0;
1016         }
1017 
1018         return rc;
1019 }
1020 
1021 
1022 
1023 // Creates a PostScript color list from a named profile data.
1024 // This is a HP extension, and it works in Lab instead of XYZ
1025 
1026 static
1027 int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
1028 {
1029     cmsHTRANSFORM xform;
1030     cmsHPROFILE   hLab;
1031     int i, nColors;
1032     char ColorName[32];
1033     cmsNAMEDCOLORLIST* NamedColorList;
1034 
1035     hLab  = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1036     xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
1037     if (xform == NULL) return 0;
1038 
1039     NamedColorList = cmsGetNamedColorList(xform);
1040     if (NamedColorList == NULL) return 0;
1041 
1042     _cmsIOPrintf(m, "<<\n");
1043     _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1044     _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1045     _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1046 
1047     nColors   = cmsNamedColorCount(NamedColorList);
1048 
1049 
1050     for (i=0; i < nColors; i++) {
1051 
1052         cmsUInt16Number In[1];
1053         cmsCIELab Lab;
1054 
1055         In[0] = (cmsUInt16Number) i;
1056 
1057         if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1058                 continue;
1059 
1060         cmsDoTransform(xform, In, &Lab, 1);
1061         _cmsIOPrintf(m, "  (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1062     }
1063 
1064 
1065 
1066     _cmsIOPrintf(m, ">>\n");
1067 
1068     cmsDeleteTransform(xform);
1069     cmsCloseProfile(hLab);
1070     return 1;
1071 }
1072 
1073 
1074 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1075 static
1076 cmsUInt32Number GenerateCSA(cmsContext ContextID,
1077                             cmsHPROFILE hProfile,
1078                             cmsUInt32Number Intent,
1079                             cmsUInt32Number dwFlags,
1080                             cmsIOHANDLER* mem)
1081 {
1082     cmsUInt32Number dwBytesUsed;
1083     cmsPipeline* lut = NULL;
1084     cmsStage* Matrix, *Shaper;
1085 
1086 
1087     // Is a named color profile?
1088     if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1089 
1090         if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
1091     }
1092     else {
1093 
1094 
1095         // Any profile class are allowed (including devicelink), but
1096         // output (PCS) colorspace must be XYZ or Lab
1097         cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1098 
1099         if (ColorSpace != cmsSigXYZData &&
1100             ColorSpace != cmsSigLabData) {
1101 
1102                 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
1103                 goto Error;
1104         }
1105 
1106 
1107         // Read the lut with all necessary conversion stages
1108         lut = _cmsReadInputLUT(hProfile, Intent);
1109         if (lut == NULL) goto Error;
1110 
1111 
1112         // Tone curves + matrix can be implemented without any LUT
1113         if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
1114 
1115             if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
1116 
1117         }
1118         else {
1119            // We need a LUT for the rest
1120            if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
1121         }
1122     }
1123 
1124 
1125     // Done, keep memory usage
1126     dwBytesUsed = mem ->UsedSpace;
1127 
1128     // Get rid of LUT
1129     if (lut != NULL) cmsPipelineFree(lut);
1130 
1131     // Finally, return used byte count
1132     return dwBytesUsed;
1133 
1134 Error:
1135     if (lut != NULL) cmsPipelineFree(lut);
1136     return 0;
1137 }
1138 
1139 // ------------------------------------------------------ Color Rendering Dictionary (CRD)
1140 
1141 
1142 
1143 /*
1144 
1145   Black point compensation plus chromatic adaptation:
1146 
1147   Step 1 - Chromatic adaptation
1148   =============================
1149 
1150           WPout
1151     X = ------- PQR
1152           Wpin
1153 
1154   Step 2 - Black point compensation
1155   =================================
1156 
1157           (WPout - BPout)*X - WPout*(BPin - BPout)
1158     out = ---------------------------------------
1159                         WPout - BPin
1160 
1161 
1162   Algorithm discussion
1163   ====================
1164 
1165   TransformPQR(WPin, BPin, WPout, BPout, PQR)
1166 
1167   Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1168 
1169 
1170   Algorithm             Stack 0...n
1171   ===========================================================
1172                         PQR BPout WPout BPin WPin
1173   4 index 3 get         WPin PQR BPout WPout BPin WPin
1174   div                   (PQR/WPin) BPout WPout BPin WPin
1175   2 index 3 get         WPout (PQR/WPin) BPout WPout BPin WPin
1176   mult                  WPout*(PQR/WPin) BPout WPout BPin WPin
1177 
1178   2 index 3 get         WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1179   2 index 3 get         BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1180   sub                   (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
1181   mult                  (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1182 
1183   2 index 3 get         WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1184   4 index 3 get         BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1185   3 index 3 get         BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1186 
1187   sub                   (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1188   mult                  (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1189   sub                   (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1190 
1191   3 index 3 get         BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1192   3 index 3 get         WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1193   exch
1194   sub                   (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1195   div
1196 
1197   exch pop
1198   exch pop
1199   exch pop
1200   exch pop
1201 
1202 */
1203 
1204 
1205 static
1206 void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1207 {
1208 
1209 
1210         if (lIsAbsolute) {
1211 
1212             // For absolute colorimetric intent, encode back to relative
1213             // and generate a relative Pipeline
1214 
1215             // Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1216 
1217             cmsCIEXYZ White;
1218 
1219             _cmsReadMediaWhitePoint(&White, hProfile);
1220 
1221             _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1222             _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1223 
1224             _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1225                       "/TransformPQR [\n"
1226                       "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1227                       "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1228                       "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
1229                       White.X, White.Y, White.Z);
1230             return;
1231         }
1232 
1233 
1234         _cmsIOPrintf(m,"%% Bradford Cone Space\n"
1235                  "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1236 
1237         _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1238 
1239 
1240         // No BPC
1241 
1242         if (!DoBPC) {
1243 
1244             _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"
1245                       "/TransformPQR [\n"
1246                       "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1247                       "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1248                       "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
1249         } else {
1250 
1251             // BPC
1252 
1253             _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1254                       "/TransformPQR [\n");
1255 
1256             _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "
1257                     "2 index 3 get 2 index 3 get sub mul "
1258                     "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1259                     "3 index 3 get 3 index 3 get exch sub div "
1260                     "exch pop exch pop exch pop exch pop } bind\n");
1261 
1262             _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "
1263                     "2 index 4 get 2 index 4 get sub mul "
1264                     "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1265                     "3 index 4 get 3 index 4 get exch sub div "
1266                     "exch pop exch pop exch pop exch pop } bind\n");
1267 
1268             _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "
1269                     "2 index 5 get 2 index 5 get sub mul "
1270                     "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1271                     "3 index 5 get 3 index 5 get exch sub div "
1272                     "exch pop exch pop exch pop exch pop } bind\n]\n");
1273 
1274         }
1275 
1276 
1277 }
1278 
1279 
1280 static
1281 void EmitXYZ2Lab(cmsIOHANDLER* m)
1282 {
1283     _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
1284     _cmsIOPrintf(m, "/EncodeLMN [\n");
1285     _cmsIOPrintf(m, "{ 0.964200  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1286     _cmsIOPrintf(m, "{ 1.000000  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1287     _cmsIOPrintf(m, "{ 0.824900  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1288     _cmsIOPrintf(m, "]\n");
1289     _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1290     _cmsIOPrintf(m, "/EncodeABC [\n");
1291 
1292 
1293     _cmsIOPrintf(m, "{ 116 mul  16 sub 100 div  } bind\n");
1294     _cmsIOPrintf(m, "{ 500 mul 128 add 256 div  } bind\n");
1295     _cmsIOPrintf(m, "{ 200 mul 128 add 256 div  } bind\n");
1296 
1297 
1298     _cmsIOPrintf(m, "]\n");
1299 
1300 
1301 }
1302 
1303 // Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1304 // I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1305 // space on 3D CLUT, but since space seems not to be a problem here, 33 points
1306 // would give a reasonable accurancy. Note also that CRD tables must operate in
1307 // 8 bits.
1308 
1309 static
1310 int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
1311 {
1312     cmsHPROFILE hLab;
1313     cmsHTRANSFORM xform;
1314     int i, nChannels;
1315     cmsUInt32Number OutputFormat;
1316     _cmsTRANSFORM* v;
1317     cmsPipeline* DeviceLink;
1318     cmsHPROFILE Profiles[3];
1319     cmsCIEXYZ BlackPointAdaptedToD50;
1320     cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1321     cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1322     cmsUInt32Number InFrm = TYPE_Lab_16;
1323     int RelativeEncodingIntent;
1324     cmsColorSpaceSignature ColorSpace;
1325 
1326 
1327     hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1328     if (hLab == NULL) return 0;
1329 
1330     OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
1331     nChannels    = T_CHANNELS(OutputFormat);
1332 
1333     ColorSpace = cmsGetColorSpace(hProfile);
1334 
1335     // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
1336 
1337     RelativeEncodingIntent = Intent;
1338     if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1339         RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1340 
1341 
1342     // Use V4 Lab always
1343     Profiles[0] = hLab;
1344     Profiles[1] = hProfile;
1345 
1346     xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
1347                                               Profiles, 2, TYPE_Lab_DBL,
1348                                               OutputFormat, RelativeEncodingIntent, 0);
1349     cmsCloseProfile(hLab);
1350 
1351     if (xform == NULL) {
1352 
1353         cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
1354         return 0;
1355     }
1356 
1357     // Get a copy of the internal devicelink
1358     v = (_cmsTRANSFORM*) xform;
1359     DeviceLink = cmsPipelineDup(v ->Lut);
1360     if (DeviceLink == NULL) return 0;
1361 
1362 
1363     // We need a CLUT
1364     dwFlags |= cmsFLAGS_FORCE_CLUT;
1365     _cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
1366 
1367     _cmsIOPrintf(m, "<<\n");
1368     _cmsIOPrintf(m, "/ColorRenderingType 1\n");
1369 
1370 
1371     cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
1372 
1373     // Emit headers, etc.
1374     EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1375     EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1376     EmitXYZ2Lab(m);
1377 
1378 
1379     // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
1380     // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
1381     // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1382     // zero. This would sacrifice a bit of highlights, but failure to do so would cause
1383     // scum dot. Ouch.
1384 
1385     if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1386             lFixWhite = FALSE;
1387 
1388     _cmsIOPrintf(m, "/RenderTable ");
1389 
1390 
1391     WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);
1392 
1393     _cmsIOPrintf(m, " %d {} bind ", nChannels);
1394 
1395     for (i=1; i < nChannels; i++)
1396             _cmsIOPrintf(m, "dup ");
1397 
1398     _cmsIOPrintf(m, "]\n");
1399 
1400 
1401     EmitIntent(m, Intent);
1402 
1403     _cmsIOPrintf(m, ">>\n");
1404 
1405     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1406 
1407         _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");
1408     }
1409 
1410     cmsPipelineFree(DeviceLink);
1411     cmsDeleteTransform(xform);
1412 
1413     return 1;
1414 }
1415 
1416 
1417 // Builds a ASCII string containing colorant list in 0..1.0 range
1418 static
1419 void BuildColorantList(char *Colorant, int nColorant, cmsUInt16Number Out[])
1420 {
1421     char Buff[32];
1422     int j;
1423 
1424     Colorant[0] = 0;
1425     if (nColorant > cmsMAXCHANNELS)
1426         nColorant = cmsMAXCHANNELS;
1427 
1428     for (j=0; j < nColorant; j++) {
1429 
1430                 sprintf(Buff, "%.3f", Out[j] / 65535.0);
1431                 strcat(Colorant, Buff);
1432                 if (j < nColorant -1)
1433                         strcat(Colorant, " ");
1434 
1435         }
1436 }
1437 
1438 
1439 // Creates a PostScript color list from a named profile data.
1440 // This is a HP extension.
1441 
1442 static
1443 int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cmsUInt32Number dwFlags)
1444 {
1445     cmsHTRANSFORM xform;
1446     int i, nColors, nColorant;
1447     cmsUInt32Number OutputFormat;
1448     char ColorName[32];
1449     char Colorant[128];
1450     cmsNAMEDCOLORLIST* NamedColorList;
1451 
1452 
1453     OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
1454     nColorant    = T_CHANNELS(OutputFormat);
1455 
1456 
1457     xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
1458     if (xform == NULL) return 0;
1459 
1460 
1461     NamedColorList = cmsGetNamedColorList(xform);
1462     if (NamedColorList == NULL) return 0;
1463 
1464     _cmsIOPrintf(m, "<<\n");
1465     _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
1466     _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1467     _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1468 
1469     nColors   = cmsNamedColorCount(NamedColorList);
1470 
1471     for (i=0; i < nColors; i++) {
1472 
1473         cmsUInt16Number In[1];
1474         cmsUInt16Number Out[cmsMAXCHANNELS];
1475 
1476         In[0] = (cmsUInt16Number) i;
1477 
1478         if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1479                 continue;
1480 
1481         cmsDoTransform(xform, In, Out, 1);
1482         BuildColorantList(Colorant, nColorant, Out);
1483         _cmsIOPrintf(m, "  (%s) [ %s ]\n", ColorName, Colorant);
1484     }
1485 
1486     _cmsIOPrintf(m, "   >>");
1487 
1488     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1489 
1490     _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");
1491     }
1492 
1493     cmsDeleteTransform(xform);
1494     return 1;
1495 }
1496 
1497 
1498 
1499 // This one does create a Color Rendering Dictionary.
1500 // CRD are always LUT-Based, no matter if profile is
1501 // implemented as matrix-shaper.
1502 
1503 static
1504 cmsUInt32Number  GenerateCRD(cmsContext ContextID,
1505                              cmsHPROFILE hProfile,
1506                              cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1507                              cmsIOHANDLER* mem)
1508 {
1509     cmsUInt32Number dwBytesUsed;
1510 
1511     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1512 
1513         EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1514     }
1515 
1516 
1517     // Is a named color profile?
1518     if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1519 
1520         if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1521             return 0;
1522         }
1523     }
1524     else {
1525 
1526         // CRD are always implemented as LUT
1527 
1528         if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1529             return 0;
1530         }
1531     }
1532 
1533     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1534 
1535         _cmsIOPrintf(mem, "%%%%EndResource\n");
1536         _cmsIOPrintf(mem, "\n%% CRD End\n");
1537     }
1538 
1539     // Done, keep memory usage
1540     dwBytesUsed = mem ->UsedSpace;
1541 
1542     // Finally, return used byte count
1543     return dwBytesUsed;
1544 
1545     cmsUNUSED_PARAMETER(ContextID);
1546 }
1547 
1548 
1549 
1550 
1551 cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
1552                                                                cmsPSResourceType Type,
1553                                                                cmsHPROFILE hProfile,
1554                                                                cmsUInt32Number Intent,
1555                                                                cmsUInt32Number dwFlags,
1556                                                                cmsIOHANDLER* io)
1557 {
1558     cmsUInt32Number  rc;
1559 
1560 
1561     switch (Type) {
1562 
1563         case cmsPS_RESOURCE_CSA:
1564             rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
1565             break;
1566 
1567         default:
1568         case cmsPS_RESOURCE_CRD:
1569             rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
1570             break;
1571     }
1572 
1573     return rc;
1574 }
1575 
1576 
1577 
1578 cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
1579                               cmsHPROFILE hProfile,
1580                               cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1581                               void* Buffer, cmsUInt32Number dwBufferLen)
1582 {
1583     cmsIOHANDLER* mem;
1584     cmsUInt32Number dwBytesUsed;
1585 
1586     // Set up the serialization engine
1587     if (Buffer == NULL)
1588         mem = cmsOpenIOhandlerFromNULL(ContextID);
1589     else
1590         mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1591 
1592     if (!mem) return 0;
1593 
1594     dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
1595 
1596     // Get rid of memory stream
1597     cmsCloseIOhandler(mem);
1598 
1599     return dwBytesUsed;
1600 }
1601 
1602 
1603 
1604 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1605 cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
1606                                               cmsHPROFILE hProfile,
1607                                               cmsUInt32Number Intent,
1608                                               cmsUInt32Number dwFlags,
1609                                               void* Buffer,
1610                                               cmsUInt32Number dwBufferLen)
1611 {
1612     cmsIOHANDLER* mem;
1613     cmsUInt32Number dwBytesUsed;
1614 
1615     if (Buffer == NULL)
1616         mem = cmsOpenIOhandlerFromNULL(ContextID);
1617     else
1618         mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1619 
1620     if (!mem) return 0;
1621 
1622     dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
1623 
1624     // Get rid of memory stream
1625     cmsCloseIOhandler(mem);
1626 
1627     return dwBytesUsed;
1628 
1629 }