Removed the libs directory containing win32 compiled versions of libpng, libtiff...
[openjpeg.git] / thirdparty / liblcms2 / src / cmsps2.c
1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2008 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
473     if (Table ->nEntries <= 0) return;  // Empty table
474
475     // Suppress whole if identity
476     if (cmsIsToneCurveLinear(Table)) return;
477
478     // Check if is really an exponential. If so, emit "exp"
479         gamma = cmsEstimateGamma(Table, 0.001);
480      if (gamma > 0) {
481             _cmsIOPrintf(m, "{ %g exp } bind ", gamma);
482             return;
483      }
484
485     _cmsIOPrintf(m, "{ ");
486
487     // Bounds check
488     EmitRangeCheck(m);
489     
490     // Emit intepolation code
491
492     // PostScript code                      Stack
493     // ===============                      ========================
494                                             // v
495     _cmsIOPrintf(m, " [");
496
497     for (i=0; i < Table->nEntries; i++) {
498                 _cmsIOPrintf(m, "%d ", Table->Table16[i]);
499     }
500
501     _cmsIOPrintf(m, "] ");                        // v tab
502
503     _cmsIOPrintf(m, "dup ");                      // v tab tab        
504     _cmsIOPrintf(m, "length 1 sub ");             // v tab dom
505     _cmsIOPrintf(m, "3 -1 roll ");                // tab dom v
506     _cmsIOPrintf(m, "mul ");                      // tab val2
507     _cmsIOPrintf(m, "dup ");                      // tab val2 val2
508     _cmsIOPrintf(m, "dup ");                      // tab val2 val2 val2
509     _cmsIOPrintf(m, "floor cvi ");                // tab val2 val2 cell0
510     _cmsIOPrintf(m, "exch ");                     // tab val2 cell0 val2
511     _cmsIOPrintf(m, "ceiling cvi ");              // tab val2 cell0 cell1
512     _cmsIOPrintf(m, "3 index ");                  // tab val2 cell0 cell1 tab 
513     _cmsIOPrintf(m, "exch ");                     // tab val2 cell0 tab cell1
514     _cmsIOPrintf(m, "get ");                      // tab val2 cell0 y1
515     _cmsIOPrintf(m, "4 -1 roll ");                // val2 cell0 y1 tab
516     _cmsIOPrintf(m, "3 -1 roll ");                // val2 y1 tab cell0 
517     _cmsIOPrintf(m, "get ");                      // val2 y1 y0 
518     _cmsIOPrintf(m, "dup ");                      // val2 y1 y0 y0
519     _cmsIOPrintf(m, "3 1 roll ");                 // val2 y0 y1 y0 
520     _cmsIOPrintf(m, "sub ");                      // val2 y0 (y1-y0)
521     _cmsIOPrintf(m, "3 -1 roll ");                // y0 (y1-y0) val2
522     _cmsIOPrintf(m, "dup ");                      // y0 (y1-y0) val2 val2
523     _cmsIOPrintf(m, "floor cvi ");                // y0 (y1-y0) val2 floor(val2) 
524     _cmsIOPrintf(m, "sub ");                      // y0 (y1-y0) rest
525     _cmsIOPrintf(m, "mul ");                      // y0 t1
526     _cmsIOPrintf(m, "add ");                      // y
527     _cmsIOPrintf(m, "65535 div ");                // result
528
529     _cmsIOPrintf(m, " } bind ");
530 }
531
532
533 // Compare gamma table
534
535 static
536 cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, int nEntries)
537 {    
538     return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0;
539 }
540
541
542 // Does write a set of gamma curves
543
544 static
545 void EmitNGamma(cmsIOHANDLER* m, int n, cmsToneCurve* g[])                  
546 {
547     int i;
548     
549     for( i=0; i < n; i++ )
550     {                
551                 if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) {
552
553             _cmsIOPrintf(m, "dup ");
554         }
555         else {    
556                         Emit1Gamma(m, g[i]);
557         }
558     }
559     
560 }
561
562
563
564
565
566 // Following code dumps a LUT onto memory stream
567         
568
569 // This is the sampler. Intended to work in SAMPLER_INSPECT mode,
570 // that is, the callback will be called for each knot with
571 //
572 //          In[]  The grid location coordinates, normalized to 0..ffff
573 //          Out[] The Pipeline values, normalized to 0..ffff
574 //
575 //  Returning a value other than 0 does terminate the sampling process
576 //
577 //  Each row contains Pipeline values for all but first component. So, I 
578 //  detect row changing by keeping a copy of last value of first 
579 //  component. -1 is used to mark begining of whole block.
580
581 static
582 int OutputValueSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
583 {
584     cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;
585     cmsUInt32Number i;
586
587
588     if (sc -> FixWhite) {
589
590         if (In[0] == 0xFFFF) {  // Only in L* = 100, ab = [-8..8]
591
592             if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&
593                 (In[2] >= 0x7800 && In[2] <= 0x8800)) {
594
595                 cmsUInt16Number* Black;
596                 cmsUInt16Number* White;
597                 cmsUInt32Number nOutputs;
598
599                 if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))
600                         return 0;
601
602                 for (i=0; i < nOutputs; i++)
603                         Out[i] = White[i];
604             }
605
606              
607         }
608     }
609
610
611     // Hadle the parenthesis on rows
612
613     if (In[0] != sc ->FirstComponent) {
614             
615             if (sc ->FirstComponent != -1) {
616
617                     _cmsIOPrintf(sc ->m, sc ->PostMin);
618                     sc ->SecondComponent = -1;
619                     _cmsIOPrintf(sc ->m, sc ->PostMaj);           
620             }
621
622             // Begin block  
623             _cmsPSActualColumn = 0;
624                     
625             _cmsIOPrintf(sc ->m, sc ->PreMaj);            
626             sc ->FirstComponent = In[0]; 
627     }
628
629
630       if (In[1] != sc ->SecondComponent) {
631             
632             if (sc ->SecondComponent != -1) {
633
634                     _cmsIOPrintf(sc ->m, sc ->PostMin);           
635             }
636                     
637             _cmsIOPrintf(sc ->m, sc ->PreMin);            
638             sc ->SecondComponent = In[1]; 
639     }
640
641           // Dump table. 
642
643           for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {
644
645                   cmsUInt16Number wWordOut = Out[i];
646           cmsUInt8Number wByteOut;           // Value as byte
647                  
648
649                   // We always deal with Lab4
650                   
651                   wByteOut = Word2Byte(wWordOut);
652                   WriteByte(sc -> m, wByteOut);
653           }
654
655           return 1;
656 }
657
658 // Writes a Pipeline on memstream. Could be 8 or 16 bits based
659
660 static
661 void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj, 
662                                              const char* PostMaj,
663                                              const char* PreMin,
664                                              const char* PostMin,                                             
665                                              int FixWhite,
666                                              cmsColorSpaceSignature ColorSpace)
667 {
668     cmsUInt32Number i;
669     cmsPsSamplerCargo sc;
670
671     sc.FirstComponent = -1;
672     sc.SecondComponent = -1;
673         sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
674     sc.m   = m;    
675     sc.PreMaj = PreMaj;
676     sc.PostMaj= PostMaj;
677
678     sc.PreMin   = PreMin;
679     sc.PostMin  = PostMin;    
680     sc.FixWhite = FixWhite;
681     sc.ColorSpace = ColorSpace;
682
683     _cmsIOPrintf(m, "[");
684
685         for (i=0; i < sc.Pipeline->Params->nInputs; i++)
686                 _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
687
688     _cmsIOPrintf(m, " [\n");
689
690     cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT);
691     
692     _cmsIOPrintf(m, PostMin);
693     _cmsIOPrintf(m, PostMaj);
694     _cmsIOPrintf(m, "] ");
695
696 }
697
698
699 // Dumps CIEBasedA Color Space Array
700
701 static
702 int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
703 {
704
705         _cmsIOPrintf(m, "[ /CIEBasedA\n");
706         _cmsIOPrintf(m, "  <<\n");
707
708         _cmsIOPrintf(m, "/DecodeA ");
709
710         Emit1Gamma(m, Curve);
711
712         _cmsIOPrintf(m, " \n");
713
714         _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
715         _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
716
717         EmitWhiteBlackD50(m, BlackPoint);
718         EmitIntent(m, INTENT_PERCEPTUAL);
719
720         _cmsIOPrintf(m, ">>\n");        
721         _cmsIOPrintf(m, "]\n");
722
723         return 1;
724 }
725
726
727 // Dumps CIEBasedABC Color Space Array
728
729 static
730 int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)
731 {
732         int i;
733         
734         _cmsIOPrintf(m, "[ /CIEBasedABC\n");
735         _cmsIOPrintf(m, "<<\n");
736         _cmsIOPrintf(m, "/DecodeABC [ ");
737
738         EmitNGamma(m, 3, CurveSet);
739
740         _cmsIOPrintf(m, "]\n");
741
742         _cmsIOPrintf(m, "/MatrixABC [ " );
743
744         for( i=0; i < 3; i++ ) {
745
746         _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],
747                                            Matrix[i + 3*1],
748                                            Matrix[i + 3*2]);      
749         }
750
751
752         _cmsIOPrintf(m, "]\n");
753
754         _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
755
756         EmitWhiteBlackD50(m, BlackPoint);
757         EmitIntent(m, INTENT_PERCEPTUAL);
758
759         _cmsIOPrintf(m, ">>\n");
760         _cmsIOPrintf(m, "]\n");
761
762
763         return 1;
764 }
765
766
767 static
768 int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXYZ* BlackPoint)
769 {
770     const char* PreMaj;
771     const char* PostMaj;
772     const char* PreMin, *PostMin;
773         cmsStage* mpe;
774
775         mpe = Pipeline ->Elements;
776
777
778         switch (cmsStageInputChannels(mpe)) {
779     case 3:
780
781             _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
782             PreMaj ="<"; 
783             PostMaj= ">\n";
784             PreMin = PostMin = "";
785             break;
786     case 4:
787             _cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
788             PreMaj = "[";
789             PostMaj = "]\n";
790             PreMin = "<";
791             PostMin = ">\n";
792             break;
793     default:
794             return 0;
795
796     }
797
798     _cmsIOPrintf(m, "<<\n");
799
800         if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
801                 
802         _cmsIOPrintf(m, "/DecodeDEF [ ");
803                 EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
804         _cmsIOPrintf(m, "]\n");
805
806                 mpe = mpe ->Next;
807     }
808
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
826     return 1;
827 }
828
829 // Generates a curve from a gray profile
830
831 static
832 cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
833 {
834     cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
835     cmsHPROFILE hXYZ  = cmsCreateXYZProfile();
836     cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
837     int i;
838
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     cmsDeleteTransform(xform);
850     cmsCloseProfile(hXYZ);
851     return Out;
852 }
853
854
855
856 // Because PostScript has only 8 bits in /Table, we should use
857 // a more perceptually uniform space... I do choose Lab.
858
859 static
860 int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
861 {
862     cmsHPROFILE hLab;
863     cmsHTRANSFORM xform;
864     cmsUInt32Number nChannels;
865     cmsUInt32Number InputFormat;
866     int rc;
867     cmsHPROFILE Profiles[2];
868     cmsCIEXYZ BlackPointAdaptedToD50;
869
870     // Does create a device-link based transform. 
871     // The DeviceLink is next dumped as working CSA.
872     
873     InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
874     nChannels   = T_CHANNELS(InputFormat);
875
876         
877         cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
878
879         // Adjust output to Lab4 
880     hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
881
882         Profiles[0] = hProfile;
883         Profiles[1] = hLab;
884
885         xform = cmsCreateMultiprofileTransform(Profiles, 2,  InputFormat, TYPE_Lab_DBL, Intent, 0);
886         cmsCloseProfile(hLab);
887         
888         if (xform == NULL) {
889
890                 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
891                 return 0;
892         }
893     
894     // Only 1, 3 and 4 channels are allowed
895
896     switch (nChannels) {
897
898     case 1: {            
899                     cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
900             EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);            
901             cmsFreeToneCurve(Gray2Y);            
902             }
903             break;
904
905     case 3: 
906     case 4: {
907                     cmsUInt32Number OutFrm = TYPE_Lab_16;
908             cmsPipeline* DeviceLink;
909             _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
910
911                         DeviceLink = cmsPipelineDup(v ->Lut);
912                         if (DeviceLink == NULL) return 0;
913
914                         dwFlags |= cmsFLAGS_FORCE_CLUT;
915                         _cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
916             
917             rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
918             cmsPipelineFree(DeviceLink);            
919             }
920             break;
921
922     default:
923
924                 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
925         return 0;
926     }
927     
928
929     cmsDeleteTransform(xform);
930     
931     return 1;
932 }
933
934 static
935 cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)
936 {
937     _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
938
939     return Data -> Double;
940 }
941
942
943 // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
944
945 static
946 int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
947 {
948     cmsColorSpaceSignature ColorSpace;    
949     int rc;
950     cmsCIEXYZ BlackPointAdaptedToD50;
951
952     ColorSpace = cmsGetColorSpace(hProfile);
953     
954     cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
955
956     if (ColorSpace == cmsSigGrayData) {
957             
958                     cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
959             rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
960         
961     }
962     else
963         if (ColorSpace == cmsSigRgbData) {
964                                 
965             cmsMAT3 Mat;
966             int i, j;
967
968             memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
969
970             for (i=0; i < 3; i++)
971                 for (j=0; j < 3; j++)
972                     Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
973
974             rc = EmitCIEBasedABC(m,  (cmsFloat64Number *) &Mat, 
975                                                 _cmsStageGetPtrToCurveSet(Shaper), 
976                                                                         &BlackPointAdaptedToD50);      
977         }
978         else  {
979
980                         cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
981             return 0;
982         }
983     
984     return rc;
985 }
986
987
988
989 // Creates a PostScript color list from a named profile data. 
990 // This is a HP extension, and it works in Lab instead of XYZ
991
992 static
993 int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
994 {
995     cmsHTRANSFORM xform;
996     cmsHPROFILE   hLab;
997     int i, nColors;
998     char ColorName[32];
999     cmsNAMEDCOLORLIST* NamedColorList;
1000
1001         hLab  = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1002     xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
1003     if (xform == NULL) return 0;
1004
1005         NamedColorList = cmsGetNamedColorList(xform);
1006     if (NamedColorList == NULL) return 0;
1007
1008     _cmsIOPrintf(m, "<<\n");
1009     _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1010     _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1011     _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1012
1013     nColors   = cmsNamedColorCount(NamedColorList);
1014
1015
1016     for (i=0; i < nColors; i++) {
1017         
1018         cmsUInt16Number In[1];
1019         cmsCIELab Lab;
1020
1021         In[0] = (cmsUInt16Number) i;
1022
1023         if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1024                 continue;
1025
1026         cmsDoTransform(xform, In, &Lab, 1);     
1027         _cmsIOPrintf(m, "  (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1028     }
1029
1030
1031         
1032     _cmsIOPrintf(m, ">>\n");
1033
1034     cmsDeleteTransform(xform);
1035     cmsCloseProfile(hLab);
1036     return 1;
1037 }
1038
1039
1040 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1041 static
1042 cmsUInt32Number GenerateCSA(cmsContext ContextID, 
1043                                                     cmsHPROFILE hProfile, 
1044                                                     cmsUInt32Number Intent, 
1045                                                     cmsUInt32Number dwFlags, 
1046                                                     cmsIOHANDLER* mem)
1047 {       
1048         cmsUInt32Number dwBytesUsed;
1049         cmsPipeline* lut = NULL;
1050         cmsStage* Matrix, *Shaper;
1051
1052
1053         // Is a named color profile?
1054         if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1055
1056                 if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
1057         }
1058         else {
1059
1060
1061                 // Any profile class are allowed (including devicelink), but
1062                 // output (PCS) colorspace must be XYZ or Lab
1063                 cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1064
1065                 if (ColorSpace != cmsSigXYZData &&
1066                         ColorSpace != cmsSigLabData) {
1067
1068                                 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
1069                                 goto Error;
1070                 }
1071
1072
1073                 // Read the lut with all necessary conversion stages
1074                 lut = _cmsReadInputLUT(hProfile, Intent);
1075                 if (lut == NULL) goto Error;
1076
1077
1078                 // Tone curves + matrix can be implemented without any LUT
1079                 if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
1080
1081                         if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
1082
1083                 }
1084                 else {
1085                    // We need a LUT for the rest
1086                    if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
1087                 }
1088         }
1089
1090
1091         // Done, keep memory usage
1092         dwBytesUsed = mem ->UsedSpace;
1093
1094         // Get rid of LUT
1095         if (lut != NULL) cmsPipelineFree(lut);
1096
1097         // Finally, return used byte count
1098         return dwBytesUsed;
1099
1100 Error:
1101         if (lut != NULL) cmsPipelineFree(lut);
1102         return 0;       
1103 }
1104
1105 // ------------------------------------------------------ Color Rendering Dictionary (CRD)
1106
1107
1108
1109 /*
1110
1111   Black point compensation plus chromatic adaptation:
1112
1113   Step 1 - Chromatic adaptation
1114   =============================
1115
1116           WPout
1117     X = ------- PQR
1118           Wpin
1119
1120   Step 2 - Black point compensation
1121   =================================
1122
1123           (WPout - BPout)*X - WPout*(BPin - BPout)
1124     out = --------------------------------------- 
1125                         WPout - BPin
1126
1127
1128   Algorithm discussion
1129   ====================
1130       
1131   TransformPQR(WPin, BPin, WPout, BPout, PQR)
1132
1133   Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1134
1135
1136   Algorithm             Stack 0...n
1137   ===========================================================
1138                         PQR BPout WPout BPin WPin
1139   4 index 3 get         WPin PQR BPout WPout BPin WPin  
1140   div                   (PQR/WPin) BPout WPout BPin WPin   
1141   2 index 3 get         WPout (PQR/WPin) BPout WPout BPin WPin   
1142   mult                  WPout*(PQR/WPin) BPout WPout BPin WPin   
1143   
1144   2 index 3 get         WPout WPout*(PQR/WPin) BPout WPout BPin WPin   
1145   2 index 3 get         BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin     
1146   sub                   (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin    
1147   mult                  (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin   
1148           
1149   2 index 3 get         WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin     
1150   4 index 3 get         BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin     
1151   3 index 3 get         BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1152   
1153   sub                   (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin     
1154   mult                  (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin     
1155   sub                   (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin     
1156
1157   3 index 3 get         BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin     
1158   3 index 3 get         WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin     
1159   exch
1160   sub                   (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin       
1161   div                
1162   
1163   exch pop 
1164   exch pop
1165   exch pop
1166   exch pop
1167
1168 */
1169
1170
1171 static
1172 void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1173 {
1174
1175    
1176         if (lIsAbsolute) {
1177
1178             // For absolute colorimetric intent, encode back to relative 
1179                         // and generate a relative Pipeline
1180
1181                         // Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1182
1183                         cmsCIEXYZ White;
1184
1185                         _cmsReadMediaWhitePoint(&White, hProfile);
1186
1187                         _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1188             _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1189
1190             _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1191                       "/TransformPQR [\n"
1192                       "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1193                       "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1194                       "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n", 
1195                                           White.X, White.Y, White.Z);
1196             return;
1197         }
1198
1199
1200         _cmsIOPrintf(m,"%% Bradford Cone Space\n"
1201                  "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1202
1203         _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1204
1205
1206         // No BPC
1207
1208         if (!DoBPC) {
1209
1210             _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"
1211                       "/TransformPQR [\n"
1212                       "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1213                       "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1214                       "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n"); 
1215         } else {
1216
1217             // BPC
1218
1219             _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1220                       "/TransformPQR [\n");
1221                   
1222             _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "
1223                     "2 index 3 get 2 index 3 get sub mul "                          
1224                     "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1225                     "3 index 3 get 3 index 3 get exch sub div "
1226                     "exch pop exch pop exch pop exch pop } bind\n");
1227
1228             _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "
1229                     "2 index 4 get 2 index 4 get sub mul "
1230                     "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1231                     "3 index 4 get 3 index 4 get exch sub div "
1232                     "exch pop exch pop exch pop exch pop } bind\n");
1233
1234             _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "
1235                     "2 index 5 get 2 index 5 get sub mul "
1236                     "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1237                     "3 index 5 get 3 index 5 get exch sub div "
1238                     "exch pop exch pop exch pop exch pop } bind\n]\n");
1239
1240         }
1241           
1242         
1243 }
1244
1245
1246 static
1247 void EmitXYZ2Lab(cmsIOHANDLER* m)
1248 {
1249     _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n"); 
1250     _cmsIOPrintf(m, "/EncodeLMN [\n");
1251     _cmsIOPrintf(m, "{ 0.964200  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1252     _cmsIOPrintf(m, "{ 1.000000  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1253     _cmsIOPrintf(m, "{ 0.824900  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1254     _cmsIOPrintf(m, "]\n");
1255     _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1256     _cmsIOPrintf(m, "/EncodeABC [\n");
1257     
1258         
1259     _cmsIOPrintf(m, "{ 116 mul  16 sub 100 div  } bind\n");
1260     _cmsIOPrintf(m, "{ 500 mul 128 add 256 div  } bind\n");
1261     _cmsIOPrintf(m, "{ 200 mul 128 add 256 div  } bind\n");
1262     
1263     
1264     _cmsIOPrintf(m, "]\n");
1265     
1266
1267 }
1268
1269 // Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1270 // I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1271 // space on 3D CLUT, but since space seems not to be a problem here, 33 points
1272 // would give a reasonable accurancy. Note also that CRD tables must operate in 
1273 // 8 bits.
1274
1275 static
1276 int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
1277 {
1278     cmsHPROFILE hLab;
1279     cmsHTRANSFORM xform;
1280     int i, nChannels;
1281     cmsUInt32Number OutputFormat;
1282     _cmsTRANSFORM* v;
1283     cmsPipeline* DeviceLink;
1284     cmsHPROFILE Profiles[3];
1285     cmsCIEXYZ BlackPointAdaptedToD50;
1286     cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1287     cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1288         cmsUInt32Number InFrm = TYPE_Lab_16;
1289         int RelativeEncodingIntent;
1290         cmsColorSpaceSignature ColorSpace;
1291     
1292     
1293         hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1294         if (hLab == NULL) return 0;
1295
1296     OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
1297         nChannels    = T_CHANNELS(OutputFormat);
1298
1299         ColorSpace = cmsGetColorSpace(hProfile);
1300
1301         // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
1302
1303     RelativeEncodingIntent = Intent;
1304         if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1305                 RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1306
1307
1308         // Use V4 Lab always
1309         Profiles[0] = hLab;
1310         Profiles[1] = hProfile;
1311
1312         xform = cmsCreateMultiprofileTransformTHR(m ->ContextID, 
1313                                                       Profiles, 2, TYPE_Lab_DBL, 
1314                                                       OutputFormat, RelativeEncodingIntent, 0);
1315         cmsCloseProfile(hLab);
1316
1317     if (xform == NULL) {
1318                         
1319                 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
1320         return 0;
1321     }
1322
1323     // Get a copy of the internal devicelink
1324     v = (_cmsTRANSFORM*) xform;
1325     DeviceLink = cmsPipelineDup(v ->Lut);
1326         if (DeviceLink == NULL) return 0;
1327    
1328   
1329         // We need a CLUT
1330         dwFlags |= cmsFLAGS_FORCE_CLUT;
1331         _cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
1332   
1333     _cmsIOPrintf(m, "<<\n");
1334     _cmsIOPrintf(m, "/ColorRenderingType 1\n");
1335
1336
1337     cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
1338
1339     // Emit headers, etc.
1340     EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1341     EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1342     EmitXYZ2Lab(m);
1343         
1344    
1345     // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab 
1346     // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127, 
1347     // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1348     // zero. This would sacrifice a bit of highlights, but failure to do so would cause
1349     // scum dot. Ouch.
1350     
1351     if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1352             lFixWhite = FALSE;
1353
1354     _cmsIOPrintf(m, "/RenderTable ");
1355     
1356         
1357     WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);
1358     
1359     _cmsIOPrintf(m, " %d {} bind ", nChannels);
1360
1361     for (i=1; i < nChannels; i++)
1362             _cmsIOPrintf(m, "dup ");
1363
1364     _cmsIOPrintf(m, "]\n");
1365
1366         
1367     EmitIntent(m, Intent);
1368
1369     _cmsIOPrintf(m, ">>\n");
1370
1371     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1372
1373         _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");
1374     }
1375
1376     cmsPipelineFree(DeviceLink);
1377     cmsDeleteTransform(xform);
1378  
1379     return 1;   
1380 }
1381
1382
1383 // Builds a ASCII string containing colorant list in 0..1.0 range
1384 static
1385 void BuildColorantList(char *Colorant, int nColorant, cmsUInt16Number Out[])
1386 {
1387     char Buff[32];
1388     int j;
1389
1390     Colorant[0] = 0;
1391     if (nColorant > cmsMAXCHANNELS)
1392         nColorant = cmsMAXCHANNELS;
1393
1394     for (j=0; j < nColorant; j++) {
1395
1396                 sprintf(Buff, "%.3f", Out[j] / 65535.0);
1397                 strcat(Colorant, Buff);
1398                 if (j < nColorant -1) 
1399                         strcat(Colorant, " ");
1400
1401         }       
1402 }
1403
1404
1405 // Creates a PostScript color list from a named profile data. 
1406 // This is a HP extension.
1407
1408 static
1409 int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cmsUInt32Number dwFlags)
1410 {
1411     cmsHTRANSFORM xform;    
1412     int i, nColors, nColorant;
1413     cmsUInt32Number OutputFormat;
1414     char ColorName[32];
1415     char Colorant[128];
1416         cmsNAMEDCOLORLIST* NamedColorList;
1417
1418         
1419     OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
1420         nColorant    = T_CHANNELS(OutputFormat);
1421
1422         
1423     xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
1424     if (xform == NULL) return 0;
1425
1426
1427         NamedColorList = cmsGetNamedColorList(xform);
1428         if (NamedColorList == NULL) return 0;
1429
1430     _cmsIOPrintf(m, "<<\n");
1431     _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
1432     _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1433     _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1434
1435     nColors   = cmsNamedColorCount(NamedColorList);
1436     
1437     for (i=0; i < nColors; i++) {
1438         
1439         cmsUInt16Number In[1];
1440         cmsUInt16Number Out[cmsMAXCHANNELS];
1441
1442         In[0] = (cmsUInt16Number) i;
1443
1444         if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1445                 continue;
1446
1447         cmsDoTransform(xform, In, Out, 1);      
1448         BuildColorantList(Colorant, nColorant, Out);
1449         _cmsIOPrintf(m, "  (%s) [ %s ]\n", ColorName, Colorant);
1450     }
1451
1452     _cmsIOPrintf(m, "   >>");
1453
1454     if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1455
1456     _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");
1457     }
1458
1459     cmsDeleteTransform(xform);  
1460     return 1;
1461 }
1462
1463
1464
1465 // This one does create a Color Rendering Dictionary. 
1466 // CRD are always LUT-Based, no matter if profile is
1467 // implemented as matrix-shaper.
1468
1469 static
1470 cmsUInt32Number  GenerateCRD(cmsContext ContextID,
1471                                                          cmsHPROFILE hProfile, 
1472                                                          cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1473                                                          cmsIOHANDLER* mem)
1474 {    
1475         cmsUInt32Number dwBytesUsed;
1476
1477         if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1478
1479                 EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1480         }
1481
1482
1483         // Is a named color profile?
1484         if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1485
1486                 if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1487                         return 0;
1488                 }
1489         }
1490         else {
1491
1492                 // CRD are always implemented as LUT 
1493
1494                 if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1495                         return 0;
1496                 }
1497         }
1498
1499         if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1500
1501                 _cmsIOPrintf(mem, "%%%%EndResource\n");
1502                 _cmsIOPrintf(mem, "\n%% CRD End\n");
1503         }
1504
1505         // Done, keep memory usage
1506         dwBytesUsed = mem ->UsedSpace;
1507
1508         // Finally, return used byte count
1509         return dwBytesUsed;
1510
1511         cmsUNUSED_PARAMETER(ContextID);
1512 }
1513
1514
1515
1516
1517 cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,                                                                                                                      
1518                                                                                                                            cmsPSResourceType Type,
1519                                                                                                                            cmsHPROFILE hProfile, 
1520                                                                                                                            cmsUInt32Number Intent, 
1521                                                                                                                            cmsUInt32Number dwFlags, 
1522                                                                                                                            cmsIOHANDLER* io)
1523 {
1524         cmsUInt32Number  rc; 
1525
1526
1527         switch (Type) {
1528
1529                 case cmsPS_RESOURCE_CSA:
1530                         rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
1531                         break;
1532                         
1533                 default:
1534                 case cmsPS_RESOURCE_CRD:
1535                         rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
1536                         break;
1537         }
1538
1539         return rc;
1540 }
1541
1542
1543
1544 cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
1545                                   cmsHPROFILE hProfile, 
1546                               cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1547                               void* Buffer, cmsUInt32Number dwBufferLen)
1548 {
1549         cmsIOHANDLER* mem;
1550     cmsUInt32Number dwBytesUsed;
1551
1552     // Set up the serialization engine
1553         if (Buffer == NULL)
1554                 mem = cmsOpenIOhandlerFromNULL(ContextID);
1555         else
1556         mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1557
1558     if (!mem) return 0;
1559
1560         dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
1561                                                                                                                            
1562     // Get rid of memory stream
1563         cmsCloseIOhandler(mem);
1564
1565         return dwBytesUsed;
1566 }
1567
1568
1569
1570 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1571 cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, 
1572                                                                                           cmsHPROFILE hProfile, 
1573                                                                                           cmsUInt32Number Intent, 
1574                                                                                           cmsUInt32Number dwFlags, 
1575                                                                                           void* Buffer, 
1576                                                                                           cmsUInt32Number dwBufferLen)  
1577 {
1578         cmsIOHANDLER* mem;
1579     cmsUInt32Number dwBytesUsed;
1580     
1581         if (Buffer == NULL)
1582                 mem = cmsOpenIOhandlerFromNULL(ContextID);
1583         else
1584         mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1585
1586     if (!mem) return 0;
1587
1588         dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
1589                                                                                                                            
1590     // Get rid of memory stream
1591         cmsCloseIOhandler(mem);
1592
1593         return dwBytesUsed;
1594
1595 }