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