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     switch (cmsStageInputChannels(mpe)) {
 810     case 3:
 811 
 812             _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
 813             PreMaj ="<";
 814             PostMaj= ">\n";
 815             PreMin = PostMin = "";
 816             break;
 817     case 4:
 818             _cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
 819             PreMaj = "[";
 820             PostMaj = "]\n";
 821             PreMin = "<";
 822             PostMin = ">\n";
 823             break;
 824     default:
 825             return 0;
 826 
 827     }
 828 
 829     _cmsIOPrintf(m, "<<\n");
 830 
 831     if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
 832 
 833         _cmsIOPrintf(m, "/DecodeDEF [ ");
 834         EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
 835         _cmsIOPrintf(m, "]\n");
 836 
 837         mpe = mpe ->Next;
 838     }
 839 
 840     if (cmsStageType(mpe) == cmsSigCLutElemType) {
 841 
 842             _cmsIOPrintf(m, "/Table ");
 843             WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0);
 844             _cmsIOPrintf(m, "]\n");
 845     }
 846 
 847     EmitLab2XYZ(m);
 848     EmitWhiteBlackD50(m, BlackPoint);
 849     EmitIntent(m, Intent);
 850 
 851     _cmsIOPrintf(m, "   >>\n");
 852     _cmsIOPrintf(m, "]\n");
 853 
 854     return 1;
 855 }
 856 
 857 // Generates a curve from a gray profile
 858 
 859 static
 860     cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
 861 {
 862     cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
 863     cmsHPROFILE hXYZ  = cmsCreateXYZProfile();
 864     cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
 865     int i;
 866 
 867     if (Out != NULL) {
 868         for (i=0; i < 256; i++) {
 869 
 870             cmsUInt8Number Gray = (cmsUInt8Number) i;
 871             cmsCIEXYZ XYZ;
 872 
 873             cmsDoTransform(xform, &Gray, &XYZ, 1);
 874 
 875             Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);
 876         }
 877     }
 878 
 879     cmsDeleteTransform(xform);
 880     cmsCloseProfile(hXYZ);
 881     return Out;
 882 }
 883 
 884 
 885 
 886 // Because PostScript has only 8 bits in /Table, we should use
 887 // a more perceptually uniform space... I do choose Lab.
 888 
 889 static
 890 int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
 891 {
 892     cmsHPROFILE hLab;
 893     cmsHTRANSFORM xform;
 894     cmsUInt32Number nChannels;
 895     cmsUInt32Number InputFormat;
 896     int rc;
 897     cmsHPROFILE Profiles[2];
 898     cmsCIEXYZ BlackPointAdaptedToD50;
 899 
 900     // Does create a device-link based transform.
 901     // The DeviceLink is next dumped as working CSA.
 902 
 903     InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
 904     nChannels   = T_CHANNELS(InputFormat);
 905 
 906 
 907     cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
 908 
 909     // Adjust output to Lab4
 910     hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
 911 
 912     Profiles[0] = hProfile;
 913     Profiles[1] = hLab;
 914 
 915     xform = cmsCreateMultiprofileTransform(Profiles, 2,  InputFormat, TYPE_Lab_DBL, Intent, 0);
 916     cmsCloseProfile(hLab);
 917 
 918     if (xform == NULL) {
 919 
 920         cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
 921         return 0;
 922     }
 923 
 924     // Only 1, 3 and 4 channels are allowed
 925 
 926     switch (nChannels) {
 927 
 928     case 1: {
 929             cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
 930             EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
 931             cmsFreeToneCurve(Gray2Y);
 932             }
 933             break;
 934 
 935     case 3:
 936     case 4: {
 937             cmsUInt32Number OutFrm = TYPE_Lab_16;
 938             cmsPipeline* DeviceLink;
 939             _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
 940 
 941             DeviceLink = cmsPipelineDup(v ->Lut);
 942             if (DeviceLink == NULL) return 0;
 943 
 944             dwFlags |= cmsFLAGS_FORCE_CLUT;
 945             _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
 946 
 947             rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
 948             cmsPipelineFree(DeviceLink);
 949             if (rc == 0) return 0;
 950             }
 951             break;
 952 
 953     default:
 954 
 955         cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
 956         return 0;
 957     }
 958 
 959 
 960     cmsDeleteTransform(xform);
 961 
 962     return 1;
 963 }
 964 
 965 static
 966 cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)
 967 {
 968     _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
 969 
 970     return Data -> Double;
 971 }
 972 
 973 
 974 // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
 975 
 976 static
 977 int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
 978 {
 979     cmsColorSpaceSignature ColorSpace;
 980     int rc;
 981     cmsCIEXYZ BlackPointAdaptedToD50;
 982 
 983     ColorSpace = cmsGetColorSpace(hProfile);
 984 
 985     cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
 986 
 987     if (ColorSpace == cmsSigGrayData) {
 988 
 989         cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
 990         rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
 991 
 992     }
 993     else
 994         if (ColorSpace == cmsSigRgbData) {
 995 
 996             cmsMAT3 Mat;
 997             int i, j;
 998 
 999             memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
1000 
1001             for (i=0; i < 3; i++)
1002                 for (j=0; j < 3; j++)
1003                     Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
1004 
1005             rc = EmitCIEBasedABC(m,  (cmsFloat64Number *) &Mat,
1006                                 _cmsStageGetPtrToCurveSet(Shaper),
1007                                  &BlackPointAdaptedToD50);
1008         }
1009         else  {
1010 
1011             cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
1012             return 0;
1013         }
1014 
1015         return rc;
1016 }
1017 
1018 
1019 
1020 // Creates a PostScript color list from a named profile data.
1021 // This is a HP extension, and it works in Lab instead of XYZ
1022 
1023 static
1024 int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
1025 {
1026     cmsHTRANSFORM xform;
1027     cmsHPROFILE   hLab;
1028     int i, nColors;
1029     char ColorName[32];
1030     cmsNAMEDCOLORLIST* NamedColorList;
1031 
1032     hLab  = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1033     xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
1034     if (xform == NULL) return 0;
1035 
1036     NamedColorList = cmsGetNamedColorList(xform);
1037     if (NamedColorList == NULL) return 0;
1038 
1039     _cmsIOPrintf(m, "<<\n");
1040     _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1041     _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1042     _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1043 
1044     nColors   = cmsNamedColorCount(NamedColorList);
1045 
1046 
1047     for (i=0; i < nColors; i++) {
1048 
1049         cmsUInt16Number In[1];
1050         cmsCIELab Lab;
1051 
1052         In[0] = (cmsUInt16Number) i;
1053 
1054         if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1055                 continue;
1056 
1057         cmsDoTransform(xform, In, &Lab, 1);
1058         _cmsIOPrintf(m, "  (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1059     }
1060 
1061 
1062 
1063     _cmsIOPrintf(m, ">>\n");
1064 
1065     cmsDeleteTransform(xform);
1066     cmsCloseProfile(hLab);
1067     return 1;
1068 }
1069 
1070 
1071 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1072 static
1073 cmsUInt32Number GenerateCSA(cmsContext ContextID,
1074                             cmsHPROFILE hProfile,
1075                             cmsUInt32Number Intent,
1076                             cmsUInt32Number dwFlags,
1077                             cmsIOHANDLER* mem)
1078 {
1079     cmsUInt32Number dwBytesUsed;
1080     cmsPipeline* lut = NULL;
1081     cmsStage* Matrix, *Shaper;
1082 
1083 
1084     // Is a named color profile?
1085     if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1086 
1087         if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
1088     }
1089     else {
1090 
1091 
1092         // Any profile class are allowed (including devicelink), but
1093         // output (PCS) colorspace must be XYZ or Lab
1094         cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1095 
1096         if (ColorSpace != cmsSigXYZData &&
1097             ColorSpace != cmsSigLabData) {
1098 
1099                 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
1100                 goto Error;
1101         }
1102 
1103 
1104         // Read the lut with all necessary conversion stages
1105         lut = _cmsReadInputLUT(hProfile, Intent);
1106         if (lut == NULL) goto Error;
1107 
1108 
1109         // Tone curves + matrix can be implemented without any LUT
1110         if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
1111 
1112             if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
1113 
1114         }
1115         else {
1116            // We need a LUT for the rest
1117            if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
1118         }
1119     }
1120 
1121 
1122     // Done, keep memory usage
1123     dwBytesUsed = mem ->UsedSpace;
1124 
1125     // Get rid of LUT
1126     if (lut != NULL) cmsPipelineFree(lut);
1127 
1128     // Finally, return used byte count
1129     return dwBytesUsed;
1130 
1131 Error:
1132     if (lut != NULL) cmsPipelineFree(lut);
1133     return 0;
1134 }
1135 
1136 // ------------------------------------------------------ Color Rendering Dictionary (CRD)
1137 
1138 
1139 
1140 /*
1141 
1142   Black point compensation plus chromatic adaptation:
1143 
1144   Step 1 - Chromatic adaptation
1145   =============================
1146 
1147           WPout
1148     X = ------- PQR
1149           Wpin
1150 
1151   Step 2 - Black point compensation
1152   =================================
1153 
1154           (WPout - BPout)*X - WPout*(BPin - BPout)
1155     out = ---------------------------------------
1156                         WPout - BPin
1157 
1158 
1159   Algorithm discussion
1160   ====================
1161 
1162   TransformPQR(WPin, BPin, WPout, BPout, PQR)
1163 
1164   Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1165 
1166 
1167   Algorithm             Stack 0...n
1168   ===========================================================
1169                         PQR BPout WPout BPin WPin
1170   4 index 3 get         WPin PQR BPout WPout BPin WPin
1171   div                   (PQR/WPin) BPout WPout BPin WPin
1172   2 index 3 get         WPout (PQR/WPin) BPout WPout BPin WPin
1173   mult                  WPout*(PQR/WPin) BPout WPout BPin WPin
1174 
1175   2 index 3 get         WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1176   2 index 3 get         BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1177   sub                   (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
1178   mult                  (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1179 
1180   2 index 3 get         WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1181   4 index 3 get         BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1182   3 index 3 get         BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1183 
1184   sub                   (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1185   mult                  (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1186   sub                   (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1187 
1188   3 index 3 get         BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1189   3 index 3 get         WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1190   exch
1191   sub                   (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1192   div
1193 
1194   exch pop
1195   exch pop
1196   exch pop
1197   exch pop
1198 
1199 */
1200 
1201 
1202 static
1203 void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1204 {
1205 
1206 
1207         if (lIsAbsolute) {
1208 
1209             // For absolute colorimetric intent, encode back to relative
1210             // and generate a relative Pipeline
1211 
1212             // Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1213 
1214             cmsCIEXYZ White;
1215 
1216             _cmsReadMediaWhitePoint(&White, hProfile);
1217 
1218             _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1219             _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1220 
1221             _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1222                       "/TransformPQR [\n"
1223                       "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1224                       "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1225                       "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
1226                       White.X, White.Y, White.Z);
1227             return;
1228         }
1229 
1230 
1231         _cmsIOPrintf(m,"%% Bradford Cone Space\n"
1232                  "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1233 
1234         _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1235 
1236 
1237         // No BPC
1238 
1239         if (!DoBPC) {
1240 
1241             _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"
1242                       "/TransformPQR [\n"
1243                       "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1244                       "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1245                       "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
1246         } else {
1247 
1248             // BPC
1249 
1250             _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1251                       "/TransformPQR [\n");
1252 
1253             _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "
1254                     "2 index 3 get 2 index 3 get sub mul "
1255                     "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1256                     "3 index 3 get 3 index 3 get exch sub div "
1257                     "exch pop exch pop exch pop exch pop } bind\n");
1258 
1259             _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "
1260                     "2 index 4 get 2 index 4 get sub mul "
1261                     "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1262                     "3 index 4 get 3 index 4 get exch sub div "
1263                     "exch pop exch pop exch pop exch pop } bind\n");
1264 
1265             _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "
1266                     "2 index 5 get 2 index 5 get sub mul "
1267                     "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1268                     "3 index 5 get 3 index 5 get exch sub div "
1269                     "exch pop exch pop exch pop exch pop } bind\n]\n");
1270 
1271         }
1272 
1273 
1274 }
1275 
1276 
1277 static
1278 void EmitXYZ2Lab(cmsIOHANDLER* m)
1279 {
1280     _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
1281     _cmsIOPrintf(m, "/EncodeLMN [\n");
1282     _cmsIOPrintf(m, "{ 0.964200  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1283     _cmsIOPrintf(m, "{ 1.000000  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1284     _cmsIOPrintf(m, "{ 0.824900  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1285     _cmsIOPrintf(m, "]\n");
1286     _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1287     _cmsIOPrintf(m, "/EncodeABC [\n");
1288 
1289 
1290     _cmsIOPrintf(m, "{ 116 mul  16 sub 100 div  } bind\n");
1291     _cmsIOPrintf(m, "{ 500 mul 128 add 256 div  } bind\n");
1292     _cmsIOPrintf(m, "{ 200 mul 128 add 256 div  } bind\n");
1293 
1294 
1295     _cmsIOPrintf(m, "]\n");
1296 
1297 
1298 }
1299 
1300 // Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1301 // I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1302 // space on 3D CLUT, but since space seems not to be a problem here, 33 points
1303 // would give a reasonable accurancy. Note also that CRD tables must operate in
1304 // 8 bits.
1305 
1306 static
1307 int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
1308 {
1309     cmsHPROFILE hLab;
1310     cmsHTRANSFORM xform;
1311     int i, nChannels;
1312     cmsUInt32Number OutputFormat;
1313     _cmsTRANSFORM* v;
1314     cmsPipeline* DeviceLink;
1315     cmsHPROFILE Profiles[3];
1316     cmsCIEXYZ BlackPointAdaptedToD50;
1317     cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1318     cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1319     cmsUInt32Number InFrm = TYPE_Lab_16;
1320     int RelativeEncodingIntent;
1321     cmsColorSpaceSignature ColorSpace;
1322 
1323 
1324     hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1325     if (hLab == NULL) return 0;
1326 
1327     OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
1328     nChannels    = T_CHANNELS(OutputFormat);
1329 
1330     ColorSpace = cmsGetColorSpace(hProfile);
1331 
1332     // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
1333 
1334     RelativeEncodingIntent = Intent;
1335     if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1336         RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1337 
1338 
1339     // Use V4 Lab always
1340     Profiles[0] = hLab;
1341     Profiles[1] = hProfile;
1342 
1343     xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
1344                                               Profiles, 2, TYPE_Lab_DBL,
1345                                               OutputFormat, RelativeEncodingIntent, 0);
1346     cmsCloseProfile(hLab);
1347 
1348     if (xform == NULL) {
1349 
1350         cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
1351         return 0;
1352     }
1353 
1354     // Get a copy of the internal devicelink
1355     v = (_cmsTRANSFORM*) xform;
1356     DeviceLink = cmsPipelineDup(v ->Lut);
1357     if (DeviceLink == NULL) return 0;
1358 
1359 
1360     // We need a CLUT
1361     dwFlags |= cmsFLAGS_FORCE_CLUT;
1362     _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
1363 
1364     _cmsIOPrintf(m, "<<\n");
1365     _cmsIOPrintf(m, "/ColorRenderingType 1\n");
1366 
1367 
1368     cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
1369 
1370     // Emit headers, etc.
1371     EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1372     EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1373     EmitXYZ2Lab(m);
1374 
1375 
1376     // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
1377     // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
1378     // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1379     // zero. This would sacrifice a bit of highlights, but failure to do so would cause
1380     // scum dot. Ouch.
1381 
1382     if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1383             lFixWhite = FALSE;
1384 
1385     _cmsIOPrintf(m, "/RenderTable ");
1386 
1387 
1388     WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);
1389 
1390     _cmsIOPrintf(m, " %d {} bind ", nChannels);
1391 
1392     for (i=1; i < nChannels; i++)
1393             _cmsIOPrintf(m, "dup ");
1394 
1395     _cmsIOPrintf(m, "]\n");
1396 
1397 
1398     EmitIntent(m, Intent);
1399 
1400     _cmsIOPrintf(m, ">>\n");
1401 
1402     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1403 
1404         _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");
1405     }
1406 
1407     cmsPipelineFree(DeviceLink);
1408     cmsDeleteTransform(xform);
1409 
1410     return 1;
1411 }
1412 
1413 
1414 // Builds a ASCII string containing colorant list in 0..1.0 range
1415 static
1416 void BuildColorantList(char *Colorant, int nColorant, cmsUInt16Number Out[])
1417 {
1418     char Buff[32];
1419     int j;
1420 
1421     Colorant[0] = 0;
1422     if (nColorant > cmsMAXCHANNELS)
1423         nColorant = cmsMAXCHANNELS;
1424 
1425     for (j=0; j < nColorant; j++) {
1426 
1427                 sprintf(Buff, "%.3f", Out[j] / 65535.0);
1428                 strcat(Colorant, Buff);
1429                 if (j < nColorant -1)
1430                         strcat(Colorant, " ");
1431 
1432         }
1433 }
1434 
1435 
1436 // Creates a PostScript color list from a named profile data.
1437 // This is a HP extension.
1438 
1439 static
1440 int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cmsUInt32Number dwFlags)
1441 {
1442     cmsHTRANSFORM xform;
1443     int i, nColors, nColorant;
1444     cmsUInt32Number OutputFormat;
1445     char ColorName[32];
1446     char Colorant[128];
1447     cmsNAMEDCOLORLIST* NamedColorList;
1448 
1449 
1450     OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
1451     nColorant    = T_CHANNELS(OutputFormat);
1452 
1453 
1454     xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
1455     if (xform == NULL) return 0;
1456 
1457 
1458     NamedColorList = cmsGetNamedColorList(xform);
1459     if (NamedColorList == NULL) return 0;
1460 
1461     _cmsIOPrintf(m, "<<\n");
1462     _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
1463     _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1464     _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1465 
1466     nColors   = cmsNamedColorCount(NamedColorList);
1467 
1468     for (i=0; i < nColors; i++) {
1469 
1470         cmsUInt16Number In[1];
1471         cmsUInt16Number Out[cmsMAXCHANNELS];
1472 
1473         In[0] = (cmsUInt16Number) i;
1474 
1475         if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1476                 continue;
1477 
1478         cmsDoTransform(xform, In, Out, 1);
1479         BuildColorantList(Colorant, nColorant, Out);
1480         _cmsIOPrintf(m, "  (%s) [ %s ]\n", ColorName, Colorant);
1481     }
1482 
1483     _cmsIOPrintf(m, "   >>");
1484 
1485     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1486 
1487     _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");
1488     }
1489 
1490     cmsDeleteTransform(xform);
1491     return 1;
1492 }
1493 
1494 
1495 
1496 // This one does create a Color Rendering Dictionary.
1497 // CRD are always LUT-Based, no matter if profile is
1498 // implemented as matrix-shaper.
1499 
1500 static
1501 cmsUInt32Number  GenerateCRD(cmsContext ContextID,
1502                              cmsHPROFILE hProfile,
1503                              cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1504                              cmsIOHANDLER* mem)
1505 {
1506     cmsUInt32Number dwBytesUsed;
1507 
1508     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1509 
1510         EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1511     }
1512 
1513 
1514     // Is a named color profile?
1515     if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1516 
1517         if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1518             return 0;
1519         }
1520     }
1521     else {
1522 
1523         // CRD are always implemented as LUT
1524 
1525         if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1526             return 0;
1527         }
1528     }
1529 
1530     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1531 
1532         _cmsIOPrintf(mem, "%%%%EndResource\n");
1533         _cmsIOPrintf(mem, "\n%% CRD End\n");
1534     }
1535 
1536     // Done, keep memory usage
1537     dwBytesUsed = mem ->UsedSpace;
1538 
1539     cmsUNUSED_PARAMETER(ContextID);
1540 
1541     // Finally, return used byte count
1542     return dwBytesUsed;
1543 
1544 }
1545 
1546 
1547 
1548 
1549 cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
1550                                                                cmsPSResourceType Type,
1551                                                                cmsHPROFILE hProfile,
1552                                                                cmsUInt32Number Intent,
1553                                                                cmsUInt32Number dwFlags,
1554                                                                cmsIOHANDLER* io)
1555 {
1556     cmsUInt32Number  rc;
1557 
1558 
1559     switch (Type) {
1560 
1561         case cmsPS_RESOURCE_CSA:
1562             rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
1563             break;
1564 
1565         default:
1566         case cmsPS_RESOURCE_CRD:
1567             rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
1568             break;
1569     }
1570 
1571     return rc;
1572 }
1573 
1574 
1575 
1576 cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
1577                               cmsHPROFILE hProfile,
1578                               cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1579                               void* Buffer, cmsUInt32Number dwBufferLen)
1580 {
1581     cmsIOHANDLER* mem;
1582     cmsUInt32Number dwBytesUsed;
1583 
1584     // Set up the serialization engine
1585     if (Buffer == NULL)
1586         mem = cmsOpenIOhandlerFromNULL(ContextID);
1587     else
1588         mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1589 
1590     if (!mem) return 0;
1591 
1592     dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
1593 
1594     // Get rid of memory stream
1595     cmsCloseIOhandler(mem);
1596 
1597     return dwBytesUsed;
1598 }
1599 
1600 
1601 
1602 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1603 cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
1604                                               cmsHPROFILE hProfile,
1605                                               cmsUInt32Number Intent,
1606                                               cmsUInt32Number dwFlags,
1607                                               void* Buffer,
1608                                               cmsUInt32Number dwBufferLen)
1609 {
1610     cmsIOHANDLER* mem;
1611     cmsUInt32Number dwBytesUsed;
1612 
1613     if (Buffer == NULL)
1614         mem = cmsOpenIOhandlerFromNULL(ContextID);
1615     else
1616         mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1617 
1618     if (!mem) return 0;
1619 
1620     dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
1621 
1622     // Get rid of memory stream
1623     cmsCloseIOhandler(mem);
1624 
1625     return dwBytesUsed;
1626 
1627 }