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