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